3 Commits
2.1.0 ... 2.2.0

Author SHA1 Message Date
fb74d84cb7 Map names to db ident names for columns passed for ordering in paginated queries. 2023-08-09 09:16:10 -05:00
fbd20de71f Add createOrUpdateRecord and record method generators.
`createOrUpdateRecord` implements upsert: update an existing record if
it exists or create a new record if not. A new error `DbUpdateError` was
added to be raised when an existing record does exist but was not able
to be updated.
2023-08-09 09:13:12 -05:00
540d0d2f67 Fix missing import in pooling implementation. 2023-02-04 19:04:50 -06:00
4 changed files with 35 additions and 8 deletions

View File

@ -1,6 +1,6 @@
# Package
version = "2.1.0"
version = "2.2.0"
author = "Jonathan Bernard"
description = "Lightweight Postgres ORM for Nim."
license = "GPL-3.0"
@ -11,4 +11,4 @@ srcDir = "src"
# Dependencies
requires @["nim >= 1.4.0", "uuids"]
requires "https://git.jdb-software.com/jdb/nim-namespaced-logging.git"
requires "namespaced_logging >= 0.3.0"

View File

@ -107,6 +107,7 @@
##
## proc createTodoItem*(db: TodoDB, rec: TodoItem): TodoItem;
## proc updateTodoItem*(db: TodoDB, rec: TodoItem): bool;
## proc createOrUpdateTodoItem*(db: TodoDB, rec: TodoItem): bool;
## proc deleteTodoItem*(db: TodoDB, rec: TodoItem): bool;
## proc deleteTodoItem*(db: TodoDB, id: UUID): bool;
##
@ -312,6 +313,9 @@ type
records*: seq[T]
totalRecords*: int
DbUpdateError* = object of CatchableError ##\
## Error types raised when a DB modification fails.
NotFoundError* = object of CatchableError ##\
## Error type raised when no record matches a given ID
@ -366,6 +370,24 @@ proc updateRecord*[D: DbConnType, T](db: D, rec: T): bool =
return numRowsUpdated > 0;
proc createOrUpdateRecord*[D: DbConnType, T](db: D, rec: T): T =
## Create or update a record. `rec` is expected to be a `model class`_. If
## the `id` field is unset, or if there is no existing record with the given
## id, a new record is inserted. Otherwise, the existing record is updated.
##
## Note that this does not perform partial updates, all fields are updated.
let findRecordStmt = "SELECT id FROM " & tableName(rec) & " WHERE id = ?"
log().debug "createOrUpdateRecord: [" & findRecordStmt & "] id: " & $rec.id
let rows = db.getAllRows(sql(findRecordStmt), [$rec.id])
if rows.len == 0: result = createRecord(db, rec)
else:
result = rec
if not updateRecord(db, rec):
raise newException(DbUpdateError,
"unable to update " & modelName(rec) & " for id " & $rec.id)
template deleteRecord*[D: DbConnType](db: D, modelType: type, id: typed): untyped =
## Delete a record by id.
let sqlStmt = "DELETE FROM " & tableName(modelType) & " WHERE id = ?"
@ -416,7 +438,7 @@ template findRecordsWhere*[D: DbConnType](
if page.isSome:
let p = page.get
if p.orderBy.isSome:
fetchStmt &= " ORDER BY " & p.orderBy.get
fetchStmt &= " ORDER BY " & identNameToDb(p.orderBy.get)
else:
fetchStmt &= " ORDER BY id"
@ -447,7 +469,7 @@ template getAllRecords*[D: DbConnType](
if page.isSome:
let p = page.get
if p.orderBy.isSome:
fetchStmt &= " ORDER BY " & p.orderBy.get
fetchStmt &= " ORDER BY " & identNameToDb(p.orderBy.get)
else:
fetchStmt &= " ORDER BY id"
@ -486,7 +508,7 @@ template findRecordsBy*[D: DbConnType](
if page.isSome:
let p = page.get
if p.orderBy.isSome:
fetchStmt &= " ORDER BY " & p.orderBy.get
fetchStmt &= " ORDER BY " & identNameToDb(p.orderBy.get)
else:
fetchStmt &= " ORDER BY id"
@ -516,6 +538,7 @@ macro generateProcsForModels*(dbType: type, modelTypes: openarray[type]): untype
## proc deleteTodoItem*(db: TodoDB, rec: TodoItem): bool;
## proc deleteTodoItem*(db: TodoDB, id: idType): bool;
## proc updateTodoItem*(db: TodoDB, rec: TodoItem): bool;
## proc createOrUpdateTodoItem*(db: TodoDB, rec: TodoItem): bool;
##
## proc findTodoItemsWhere*(
## db: TodoDB, whereClause: string, values: varargs[string]): TodoItem;
@ -537,6 +560,7 @@ macro generateProcsForModels*(dbType: type, modelTypes: openarray[type]): untype
let findWhereName = ident("find" & pluralize(modelName) & "Where")
let createName = ident("create" & modelName)
let updateName = ident("update" & modelName)
let createOrUpdateName = ident("createOrUpdate" & modelName)
let deleteName = ident("delete" & modelName)
let idType = typeOfColumn(t, "id")
result.add quote do:
@ -560,6 +584,9 @@ macro generateProcsForModels*(dbType: type, modelTypes: openarray[type]): untype
proc `updateName`*(db: `dbType`, rec: `t`): bool =
db.withConn: result = updateRecord(conn, rec)
proc `createOrUpdateName`*(db: `dbType`, rec: `t`): `t` =
db.inTransaction: result = createOrUpdateRecord(conn, rec)
proc `deleteName`*(db: `dbType`, rec: `t`): bool =
db.withConn: result = deleteRecord(conn, rec)

View File

@ -6,8 +6,8 @@
import std/[db_common, logging, sequtils, strutils, sugar]
from std/db_sqlite import getRow
from std/db_postgres import getRow
from std/db_sqlite import getRow, close
from std/db_postgres import getRow, close
import namespaced_logging
import ./db_common as fiber_db_common

View File

@ -102,7 +102,7 @@ proc dbFormat*[T](list: seq[T]): string =
proc dbFormat*[T](item: T): string =
## For all other types, fall back on a defined `$` function to create a
## string version of the value we can include in an SQL query>
## string version of the value we can include in an SQL query.
return $item
type DbArrayParseState = enum