Finished the initial work for the generic DB layer.
This commit is contained in:
parent
1337d17105
commit
a16ef81077
@ -1,5 +1,5 @@
|
|||||||
import db_postgres, macros, options, postgres, sequtils, strutils, times,
|
import db_postgres, macros, options, postgres, sequtils, strutils,
|
||||||
timeutils, unicode, uuids
|
times, timeutils, unicode, uuids
|
||||||
|
|
||||||
import ./models
|
import ./models
|
||||||
import ./db_common
|
import ./db_common
|
||||||
@ -12,24 +12,9 @@ type
|
|||||||
proc connect*(connString: string): PMApiDb =
|
proc connect*(connString: string): PMApiDb =
|
||||||
result = PMApiDb(conn: open("", "", "", connString))
|
result = PMApiDb(conn: open("", "", "", connString))
|
||||||
|
|
||||||
macro makeGetRecord(modelType: type): untyped =
|
generateProcsForModels([User, ApiToken, Measure, Value])
|
||||||
echo modelType.getType.treeRepr
|
|
||||||
#[
|
|
||||||
let procIdent = ident("get" & $modelType.getType[1])
|
|
||||||
return quote do:
|
|
||||||
proc `procIdent`*(db: PMApiDb, id: UUID): `modelType` = return db.conn.getRecord(`modelType`, id)
|
|
||||||
]#
|
|
||||||
|
|
||||||
proc createUser*(db: PMApiDb, user: User): User = return db.conn.createRecord(user)
|
generateLookup(User, @["email"])
|
||||||
proc createApiToken*(db: PMApiDb, token: ApiToken): ApiToken = return db.conn.createRecord(token)
|
generateLookup(ApiToken, @["userId"])
|
||||||
proc createMeasure*(db: PMApiDb, measure: Measure): Measure = return db.conn.createRecord(measure)
|
generateLookup(Measure, @["userId"])
|
||||||
proc createValue*(db: PMApiDb, value: Value): Value = return db.conn.createRecord(value)
|
generateLookup(Value, @["measureId"])
|
||||||
|
|
||||||
proc getUser*(db: PMApiDb, id: UUID): User = return db.conn.getRecord(User, id)
|
|
||||||
proc getUser*(db: PMApiDb, id: string): User = return db.conn.getRecord(User, parseUUID(id))
|
|
||||||
proc getApiToken*(db: PMApiDb, id: UUID): ApiToken = return db.conn.getRecord(ApiToken, id)
|
|
||||||
proc getMeasure*(db: PMApiDb, id: UUID): Measure = return db.conn.getRecord(Measure, id)
|
|
||||||
proc getValue*(db: PMApiDb, id: UUID): Value = return db.conn.getRecord(Value, id)
|
|
||||||
|
|
||||||
#proc getUsersWhere*(db: PMApiDb, whereClause: string, values: varargs[string, dbFormat]): User =
|
|
||||||
# return db.conn.getRecordsWhere(User, whereClause, values)
|
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
import db_postgres, macros, options, sequtils, uuids
|
import db_postgres, macros, options, sequtils, strutils, uuids
|
||||||
|
|
||||||
|
from unicode import capitalize
|
||||||
|
|
||||||
import ./db_util
|
import ./db_util
|
||||||
|
|
||||||
@ -27,11 +29,11 @@ proc updateRecord*[T](db: DbConn, rec: T): bool =
|
|||||||
var mc = newMutateClauses()
|
var mc = newMutateClauses()
|
||||||
populateMutateClauses(rec, false, mc)
|
populateMutateClauses(rec, false, mc)
|
||||||
|
|
||||||
let setClause = zip(mc.columns, mc.placeholders).mapIt(it.a & " = " it.b).join(',')
|
let setClause = zip(mc.columns, mc.placeholders).mapIt(it.a & " = " & it.b).join(",")
|
||||||
let numRowsUpdated = db.execAffectedRows(sql(
|
let numRowsUpdated = db.execAffectedRows(sql(
|
||||||
"UPDATE " & tableName(rec) &
|
"UPDATE " & tableName(rec) &
|
||||||
" SET " & setClause &
|
" SET " & setClause &
|
||||||
" WHERE id = ? "), mc.values.concat(@[rec.id]))
|
" WHERE id = ? "), mc.values.concat(@[$rec.id]))
|
||||||
|
|
||||||
return numRowsUpdated > 0;
|
return numRowsUpdated > 0;
|
||||||
|
|
||||||
@ -59,3 +61,79 @@ template getAllRecords*(db: DbConn, modelType: type): untyped =
|
|||||||
" FROM " & tableName(modelType)))
|
" FROM " & tableName(modelType)))
|
||||||
.mapIt(rowToModel(modelType, it))
|
.mapIt(rowToModel(modelType, it))
|
||||||
|
|
||||||
|
template getRowsBy*(db: DbConn, modelType: type, lookups: seq[tuple[field: string, value: string]]): untyped =
|
||||||
|
db.getAllRows(sql(
|
||||||
|
"SELECT " & columnNamesForModel(modelType).join(",") &
|
||||||
|
" FROM " & tableName(modelType) &
|
||||||
|
" WHERE " & lookups.mapIt(it.field & " = ?").join(" AND ")),
|
||||||
|
lookups.mapIt(it.value))
|
||||||
|
.mapIt(rowToModel(modelType, it))
|
||||||
|
|
||||||
|
macro generateProcsForModels*(modelTypes: openarray[type]): untyped =
|
||||||
|
result = newStmtList()
|
||||||
|
|
||||||
|
for t in modelTypes:
|
||||||
|
let modelName = $(t.getType[1])
|
||||||
|
let getName = ident("get" & modelName)
|
||||||
|
let getAllName = ident("getAll" & modelName)
|
||||||
|
let getWhereName = ident("get" & modelName & "sWhere")
|
||||||
|
let createName = ident("create" & modelName)
|
||||||
|
let updateName = ident("update" & modelName)
|
||||||
|
result.add quote do:
|
||||||
|
proc `getName`*(db: PMApiDb, id: UUID): `t` = getRecord(db.conn, `t`, id)
|
||||||
|
proc `getAllName`*(db: PMApiDb): seq[`t`] = getAllRecords(db.conn, `t`)
|
||||||
|
proc `getWhereName`*(db: PMApiDb, whereClause: string, values: varargs[string, dbFormat]): seq[`t`] =
|
||||||
|
return getRecordsWhere(db.conn, `t`, whereClause, values)
|
||||||
|
proc `createName`*(db: PMApiDb, rec: `t`): `t` = createRecord(db.conn, rec)
|
||||||
|
proc `updateName`*(db: PMApiDb, rec: `t`): bool = updateRecord(db.conn, rec)
|
||||||
|
|
||||||
|
macro generateLookup*(modelType: type, fields: seq[string]): untyped =
|
||||||
|
let fieldNames = fields[1].mapIt($it)
|
||||||
|
let procName = ident("get" & $modelType.getType[1] & "By" & fieldNames.mapIt(it.capitalize).join("And"))
|
||||||
|
|
||||||
|
# Create proc skeleton
|
||||||
|
result = quote do:
|
||||||
|
proc `procName`*(db: PMApiDb): seq[`modelType`] =
|
||||||
|
return getRowsBy(db.conn, `modelType`)
|
||||||
|
|
||||||
|
var callParams = quote do: @[]
|
||||||
|
|
||||||
|
# Add dynamic parameters for the proc definition and inner proc call
|
||||||
|
for n in fieldNames:
|
||||||
|
let paramTuple = newNimNode(nnkPar)
|
||||||
|
paramTuple.add(newColonExpr(ident("field"), newLit(n)))
|
||||||
|
paramTuple.add(newColonExpr(ident("value"), ident(n)))
|
||||||
|
|
||||||
|
result[3].add(newIdentDefs(ident(n), ident("string")))
|
||||||
|
callParams[1].add(paramTuple)
|
||||||
|
|
||||||
|
result[6][0][0].add(callParams)
|
||||||
|
|
||||||
|
macro generateProcsForFieldLookups*(modelsAndFields: openarray[tuple[t: type, fields: seq[string]]]): untyped =
|
||||||
|
result = newStmtList()
|
||||||
|
|
||||||
|
for i in modelsAndFields:
|
||||||
|
var modelType = i[1][0]
|
||||||
|
let fieldNames = i[1][1][1].mapIt($it)
|
||||||
|
|
||||||
|
let procName = ident("get" & $modelType & "By" & fieldNames.mapIt(it.capitalize).join("And"))
|
||||||
|
|
||||||
|
# Create proc skeleton
|
||||||
|
let procDefAST = quote do:
|
||||||
|
proc `procName`*(db: PMApiDb): seq[`modelType`] =
|
||||||
|
return getRowsBy(db.conn, `modelType`)
|
||||||
|
|
||||||
|
var callParams = quote do: @[]
|
||||||
|
|
||||||
|
# Add dynamic parameters for the proc definition and inner proc call
|
||||||
|
for n in fieldNames:
|
||||||
|
let paramTuple = newNimNode(nnkPar)
|
||||||
|
paramTuple.add(newColonExpr(ident("field"), newLit(n)))
|
||||||
|
paramTuple.add(newColonExpr(ident("value"), ident(n)))
|
||||||
|
|
||||||
|
procDefAST[3].add(newIdentDefs(ident(n), ident("string")))
|
||||||
|
callParams[1].add(paramTuple)
|
||||||
|
|
||||||
|
procDefAST[6][0][0].add(callParams)
|
||||||
|
|
||||||
|
result.add procDefAST
|
||||||
|
@ -112,27 +112,22 @@ proc parseDbArray*(val: string): seq[string] =
|
|||||||
result.add(curStr)
|
result.add(curStr)
|
||||||
|
|
||||||
proc createParseStmt*(t, value: NimNode): NimNode =
|
proc createParseStmt*(t, value: NimNode): NimNode =
|
||||||
result = newStmtList()
|
|
||||||
|
|
||||||
#echo "Creating parse statment for ", t.treeRepr
|
#echo "Creating parse statment for ", t.treeRepr
|
||||||
if t.typeKind == ntyObject:
|
if t.typeKind == ntyObject:
|
||||||
|
|
||||||
if t.getType == UUID.getType:
|
if t.getType == UUID.getType:
|
||||||
result.add quote do:
|
result = quote do: parseUUID(`value`)
|
||||||
parseUUID(`value`)
|
|
||||||
|
|
||||||
elif t.getType == DateTime.getType:
|
elif t.getType == DateTime.getType:
|
||||||
result.add quote do:
|
result = quote do: `value`.parseIso8601
|
||||||
`value`.parseIso8601
|
|
||||||
|
|
||||||
elif t.getTypeInst == Option.getType:
|
elif t.getTypeInst == Option.getType:
|
||||||
let innerType = t.getTypeImpl[2][0][0][1]
|
let innerType = t.getTypeImpl[2][0][0][1]
|
||||||
let parseStmt = createParseStmt(innerType, value)
|
let parseStmt = createParseStmt(innerType, value)
|
||||||
result.add quote do:
|
result = quote do:
|
||||||
if `value`.len == 0:
|
if `value`.len == 0: none[`innerType`]()
|
||||||
none[`innerType`]()
|
else: some(`parseStmt`)
|
||||||
else:
|
|
||||||
some(`parseStmt`)
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
error "Unknown value object type: " & $t.getTypeInst
|
error "Unknown value object type: " & $t.getTypeInst
|
||||||
@ -142,17 +137,13 @@ proc createParseStmt*(t, value: NimNode): NimNode =
|
|||||||
|
|
||||||
let parseStmts = createParseStmt(innerType, ident("it"))
|
let parseStmts = createParseStmt(innerType, ident("it"))
|
||||||
|
|
||||||
echo parseStmts.treeRepr
|
result = quote do: parseDbArray(`value`).mapIt(`parseStmts`)
|
||||||
result.add quote do:
|
|
||||||
parseDbArray(`value`).mapIt(`parseStmts`)
|
|
||||||
|
|
||||||
elif t.typeKind == ntyString:
|
elif t.typeKind == ntyString:
|
||||||
result.add quote do:
|
result = quote do: `value`
|
||||||
`value`
|
|
||||||
|
|
||||||
elif t.typeKind == ntyInt:
|
elif t.typeKind == ntyInt:
|
||||||
result.add quote do:
|
result = quote do: parseInt(`value`)
|
||||||
parseInt(`value`)
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
error "Unknown value type: " & $t.typeKind
|
error "Unknown value type: " & $t.typeKind
|
||||||
|
Loading…
x
Reference in New Issue
Block a user