I write a web application in the nim programming language and use norm as my ORM.
There I follow the standard repository pattern, meaning you have associated with a given URL a controller which calls services that contain business logic on what should happen on the database, which call repositories to perform the actual queries.
I have a generic repository that performs various very repetitive queries for me. The following query for example fetches a list of all entries related to a "Campaign" that has a specific name:
import norm/[model, sqlite]
import <path to the file with the definitions of the used Model and newModel>
proc getCampaignList*[M: Model](connection: DbConn, campaignName: string, modelType: typedesc[M]): seq[M] =
##[ Retrieves all rows/entries for a campaign with the given name and
returns them as Model M.
``campaignName`` must be exactly equal to the name of the targetted campaign,
the comparison is case-sensitive.]##
mixin newModel
var entries: seq[M] = @[]
entries.add(newModel(M))
connection.select(entries, "campaign_id.name = ?", campaignName)
result = entries
This works fine if I call the proc directly. I have, however, also been writing generic procs that build controller-procs for me as my application is very CRUD heavy and thus very repetitive. So I have a genericService and more.
When used within these several layers of generics, the proc breaks mysteriously:
type mismatch: got <DbConn, seq[DiaryEntryRead], string, DbValue>
but expected one of:
...
<Unnecessary examples>
...
proc select[T: Model](dbConn; objs: var seq[T]; cond: string;
params: varargs[DbValue, dbValue])
first type mismatch at position: 4
required type for params: varargs[DbValue]
but expression 'dbValue(campaignName)' is of type: DbValue
expression: select(connection, entries, "campaign_id.name = ?", dbValue(campaignName))
I'm not sure why this suddenly happens when it worked perfectly fine before. What could my issue be?
CodePudding user response:
The issue appears to be somewhere in the interaction between nim generics and converting types properly into varargs (Thanks to "Solitude" and "Rika" from nim's discord server for the help!). I haven't dug any deeper into it as to why this happens, but at very least it's fixable.
The parameters you throw in as the parameters at position 4 and onwards can just be converted into DbValue and stuffed into an array or seq. Below the way I solved this and "foolproofed" the proc (I used an array because why use a seq when the number of parameters is static and you know it ahead of time):
proc getCampaignList*[M: Model](connection: MyDbConn, campaignName: string, modelType: typedesc[M]): seq[M] =
##[ Retrieves all rows/entries of a campaign with the given name and
returns them as Model M.
``campaignName`` must be exactly equal to the name of the targetted campaign,
the comparison is case-sensitive.]##
mixin newModel
var entries: seq[M] = @[]
entries.add(newModel(M))
let queryParams: array[1, DbValue] = [campaignName.dbValue()]
connection.select(entries, "campaign_id.name = ?", queryParams)
result = entries