I'm experimenting with reflections on Nim, mostly to get a better grasp of the language, seeing if I can make a prepared SQL statement given any object with any number of fields. It works, but there are a few things I still didn't manage. Here's the code:
import strutils
proc createTable[Obj](): string =
var tostr: Obj
#The SQL should be compatible with PostgreSQL and MySQL/MariaDB
var preparedSql = "CREATE OR REPLACE TABLE "
preparedSql.add($Obj)
preparedSql.add(" (")
var vartype : string
for name, field in fieldPairs(tostr):
preparedSql.add(name)
preparedSql.add(" ")
if ($field.type=="string"):
vartype = "varchar(3000)"
else:
vartype = $(field.type)
preparedSql.add(vartype)
preparedSql.add(", ")
preparedSql.removeSuffix(", ") #This could be an if check in the for loop above, but this works fine right now.
preparedSql.add(");")
result = preparedSql #Might as well use result instead of preparedSql in the code above
type Sample = object
pid : int
forkey : int
name : string
description : string
var sam = Sample(pid: 1, forkey: 2, name: "Sample", description: "Plenty of words")
echo createTable[sam]()
#The result will print: CREATE OR REPLACE TABLE Sample (pid int, forkey int, name varchar(3000), description varchar(3000));
I do think the code is much longer than needed, but I'm not entirely sure what could be trimmed/joined.
Now, there are some questions I'd like to make:
Hi Bonesinger,
That's an interesting project, I was also about to do something similar to this.
I've found a nice repo called "norm", maybe you can find some inspiration: https://github.com/moigagoo/norm/blob/develop/src/norm/model.nim
(It's acting on a special object though).
Best regards, David
You can have a look into how NESM or nim-serialization use custom tags: https://xomachine.gitlab.io/NESM/ https://github.com/status-im/nim-serialization#custom-serialization-of-user-defined-types
type
Foo = object
a: string
b {.dontSerialize.}: int
c {.dontSerialize(JSON).}: float
d {.serializedFieldName("z").}: int
proc createTable(Obj:typedesc): string =
var tostr: Obj
for fld,val in Obj.fieldpairs:
result.add(fld & ',' & $val & '\n')
3: it's possible to check custom pragmas with hasCustomPragma and getCustomPragmaVal from macros
putting it all together:
import macros,strformat,strutils
template primarykey{.pragma.}
template foreignkey(s:string){.pragma.}
proc createTable(Obj:typedesc): string =
var tostr: Obj
#The SQL should be compatible with PostgreSQL and MySQL/MariaDB
var preparedSql = &"CREATE OR REPLACE TABLE {$Obj} ("
var additions : string
for name, field in fieldPairs(tostr):
preparedSql.add name & " " & (if typeof(field) is string: "varchar(3000)" else: $typeof(field)) & ", "
when hasCustomPragma(field,primarykey): additions.add "PRIMARY KEY (" & name & "), "
elif hasCustomPragma(field,foreignkey): additions.add "FOREIGN KEY (" & name & ") REFERENCES " & getCustomPragmaVal(field,foreignkey) & "(" & name & "), "
preparedSql.add additions
preparedSql.removeSuffix(", ") #This could be an if check in the for loop above, but this works fine right now.
preparedSql.add ");"
result = preparedSql #Might as well use result instead of preparedSql in the code above
type Sample = object
pid{.primarykey.} : int
forkey{.foreignkey:"OtherTable".} : int
name : string
description : string
echo createTable(Sample)
https://play.nim-lang.org/#ix=2Toa