5 Commits
0.2.0 ... 0.3.2

Author SHA1 Message Date
2b78727356 Fix for PostgreSQL timestamp with timezone fields.
The previous fix for PostgreSQL timestamp fields matched fields with and
without timezones, but did not properly parse values from fields that
included the timezone. Now we check for the presence of the timezone in
the date string and choose a format string to parse it correctly.
2021-07-05 11:24:06 -05:00
445c86f97e Shuffle variable declarations to make functions GC-safe. 2021-07-02 20:34:29 -05:00
126167fdaf Fix name mapping in field lookups generation. 2021-07-02 20:16:49 -05:00
ff0c5e5305 Update to support Nim 1.4.x+ 2021-04-02 13:58:08 -05:00
bdd62cad66 Add support for float data type. 2020-02-16 21:50:43 -06:00
3 changed files with 28 additions and 18 deletions

View File

@ -1,6 +1,6 @@
# Package # Package
version = "0.2.0" version = "0.3.2"
author = "Jonathan Bernard" author = "Jonathan Bernard"
description = "Lightweight Postgres ORM for Nim." description = "Lightweight Postgres ORM for Nim."
license = "GPL-3.0" license = "GPL-3.0"
@ -10,4 +10,4 @@ srcDir = "src"
# Dependencies # Dependencies
requires "nim >= 1.0.4" requires "nim >= 1.4.0"

View File

@ -30,7 +30,7 @@ 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[0] & " = " & it[1]).join(",")
let numRowsUpdated = db.execAffectedRows(sql( let numRowsUpdated = db.execAffectedRows(sql(
"UPDATE " & tableName(rec) & "UPDATE " & tableName(rec) &
" SET " & setClause & " SET " & setClause &
@ -139,7 +139,7 @@ macro generateProcsForFieldLookups*(dbType: type, modelsAndFields: openarray[tup
# Add dynamic parameters for the proc definition and inner proc call # Add dynamic parameters for the proc definition and inner proc call
for n in fieldNames: for n in fieldNames:
let paramTuple = newNimNode(nnkPar) let paramTuple = newNimNode(nnkPar)
paramTuple.add(newColonExpr(ident("field"), newLit(n))) paramTuple.add(newColonExpr(ident("field"), newLit(identNameToDb(n))))
paramTuple.add(newColonExpr(ident("value"), ident(n))) paramTuple.add(newColonExpr(ident("value"), ident(n)))
procDefAST[3].add(newIdentDefs(ident(n), ident("string"))) procDefAST[3].add(newIdentDefs(ident(n), ident("string")))

View File

@ -3,14 +3,6 @@ import json, macros, options, sequtils, strutils, times, timeutils, unicode,
import nre except toSeq import nre except toSeq
const UNDERSCORE_RUNE = "_".toRunes[0]
const PG_TIMESTAMP_FORMATS = [
"yyyy-MM-dd HH:mm:sszz",
"yyyy-MM-dd HH:mm:ss'.'fffzz"
]
var PG_PARTIAL_FORMAT_REGEX = re"(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.)(\d{1,3})(\S+)?"
type type
MutateClauses* = object MutateClauses* = object
columns*: seq[string] columns*: seq[string]
@ -30,7 +22,9 @@ macro modelName*(model: object): string =
macro modelName*(modelType: type): string = macro modelName*(modelType: type): string =
return newStrLitNode($modelType.getType[1]) return newStrLitNode($modelType.getType[1])
proc identNameToDb*(name: string): string = proc identNameToDb*(name: string): string =
const UNDERSCORE_RUNE = "_".toRunes[0]
let nameInRunes = name.toRunes let nameInRunes = name.toRunes
var prev: Rune var prev: Rune
var resultRunes = newSeq[Rune]() var resultRunes = newSeq[Rune]()
@ -70,6 +64,16 @@ type DbArrayParseState = enum
expectStart, inQuote, inVal, expectEnd expectStart, inQuote, inVal, expectEnd
proc parsePGDatetime*(val: string): DateTime = proc parsePGDatetime*(val: string): DateTime =
const PG_TIMESTAMP_FORMATS = [
"yyyy-MM-dd HH:mm:ss",
"yyyy-MM-dd HH:mm:sszz",
"yyyy-MM-dd HH:mm:ss'.'fff",
"yyyy-MM-dd HH:mm:ss'.'fffzz"
]
let PG_PARTIAL_FORMAT_REGEX = re"(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.)(\d{1,3})(\S+)?"
var errStr = "" var errStr = ""
# Try to parse directly using known format strings. # Try to parse directly using known format strings.
@ -86,11 +90,14 @@ proc parsePGDatetime*(val: string): DateTime =
if match.isSome: if match.isSome:
let c = match.get.captures let c = match.get.captures
try: try:
if c.toSeq.len == 2:
let corrected = c[0] & alignLeft(c[1], 3, '0')
return corrected.parse(PG_TIMESTAMP_FORMATS[2])
else:
let corrected = c[0] & alignLeft(c[1], 3, '0') & c[2] let corrected = c[0] & alignLeft(c[1], 3, '0') & c[2]
return corrected.parse(PG_TIMESTAMP_FORMATS[1]) return corrected.parse(PG_TIMESTAMP_FORMATS[3])
except: except:
errStr &= "\n\t" & PG_TIMESTAMP_FORMATS[1] & errStr &= "\n\t" & getCurrentExceptionMsg()
" after padding out milliseconds to full 3-digits"
raise newException(ValueError, "Cannot parse PG date. Tried:" & errStr) raise newException(ValueError, "Cannot parse PG date. Tried:" & errStr)
@ -197,6 +204,9 @@ proc createParseStmt*(t, value: NimNode): NimNode =
elif t.typeKind == ntyInt: elif t.typeKind == ntyInt:
result = quote do: parseInt(`value`) result = quote do: parseInt(`value`)
elif t.typeKind == ntyFloat:
result = quote do: parseFloat(`value`)
elif t.typeKind == ntyBool: elif t.typeKind == ntyBool:
result = quote do: "true".startsWith(`value`.toLower) result = quote do: "true".startsWith(`value`.toLower)
@ -275,7 +285,7 @@ proc typeOfColumn*(modelType: NimNode, colName: string): NimNode =
proc isEmpty(val: int): bool = return val == 0 proc isEmpty(val: int): bool = return val == 0
proc isEmpty(val: UUID): bool = return val.isZero proc isEmpty(val: UUID): bool = return val.isZero
proc isEmpty(val: string): bool = return val.isNilOrWhitespace proc isEmpty(val: string): bool = return val.isEmptyOrWhitespace
macro populateMutateClauses*(t: typed, newRecord: bool, mc: var MutateClauses): untyped = macro populateMutateClauses*(t: typed, newRecord: bool, mc: var MutateClauses): untyped =