Skip to content

Commit fdb9599

Browse files
authored
Support extra schemas in Ormin imports (#85)
* Support extra schemas in Ormin imports * Support extra schemas in Ormin imports * Support extra schemas in Ormin imports * Generate database enum values * Fix schema import regressions * Reuse SQL lexer for enum discovery
1 parent c057526 commit fdb9599

11 files changed

Lines changed: 574 additions & 69 deletions

config.nims

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ task test, "Run all test suite":
2020
exec "nim c -f -r tests/tsqlite"
2121
exec "nim c -f -r tests/tdb_utils"
2222
exec "nim c -f -r tests/timportstatic"
23+
exec "nim c -f -r tests/tpostgres_schema_import"
24+
exec "nim c -f -r tests/tqualified_schema_queries"
2325

2426
task setup_postgres, "Ensure local Postgres has test DB/user":
2527
# Use a simple script to avoid Nim/psql quoting pitfalls

ormin.nimble

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Package
22

3-
version = "0.9.0"
3+
version = "0.10.0"
44
author = "Araq"
55
description = "Prepared SQL statement generator. A lightweight ORM."
66
license = "MIT"

ormin/db_types.nim

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ proc dbTypFromName*(name: string): DbTypeKind =
55
var k = dbUnknown
66

77
case name.toLowerAscii
8-
of "int", "integer", "int8", "smallint", "int16",
8+
of "int", "integer", "int2", "int4", "int8", "smallint", "bigint", "int16",
99
"longint", "int32", "int64", "tinyint", "hugeint": k = dbInt
1010
of "uint", "uint8", "uint16", "uint32", "uint64": k = dbUInt
1111
of "serial": k = dbSerial
@@ -14,7 +14,7 @@ proc dbTypFromName*(name: string): DbTypeKind =
1414
of "blob": k = dbBlob
1515
of "fixedchar": k = dbFixedChar
1616
of "varchar", "text", "string": k = dbVarchar
17-
of "json": k = dbJson
17+
of "json", "jsonb": k = dbJson
1818
of "xml": k = dbXml
1919
of "decimal": k = dbDecimal
2020
of "float", "double", "longdouble", "real": k = dbFloat

ormin/db_utils.nim

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,12 +113,12 @@ iterator tableDefs(sql: DbSql): tuple[name, tableName, model: string] =
113113
for i in 0 ..< ast.len:
114114
let node = ast[i]
115115
if node.kind in {nkCreateTable, nkCreateTableIfNotExists}:
116-
yield (node[0].strVal.toLowerAscii(), $node[0], $node)
116+
yield (sqlIdentBaseName(node[0]).toLowerAscii(), sqlIdentName(node[0]), $node)
117117
else:
118118
# Fallback: ast might be a single statement (not a list)
119119
let node = ast
120120
if node.kind in {nkCreateTable, nkCreateTableIfNotExists}:
121-
yield (node[0].strVal.toLowerAscii(), $node[0], $node)
121+
yield (sqlIdentBaseName(node[0]).toLowerAscii(), sqlIdentName(node[0]), $node)
122122

123123
iterator tablePairs*(sql: string): tuple[name, model: string] =
124124
for name, _, model in tableDefs(DbSql(sql)):

ormin/importer_core.nim

Lines changed: 76 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ type
1313
name: string
1414
tabIndex: int
1515
typ: DbTypekind
16+
typeName: string
17+
validValues: seq[string]
1618
key: int # 0 nothing special,
1719
# +1 -- primary key
1820
# -N -- references attribute N
@@ -28,6 +30,7 @@ type
2830
DbColumns* = seq[DbColumn]
2931

3032
KnownTables* = OrderedTable[string, DbColumns]
33+
KnownEnums = Table[string, seq[string]]
3134
ImportTarget* = enum
3235
postgre, sqlite, mysql
3336

@@ -40,13 +43,30 @@ proc hasRefs(colDesc: SqlNode): (string, string) =
4043
for i in 2 ..< colDesc.len:
4144
let c = colDesc[i]
4245
if c.kind == nkReferences:
43-
if c[0].kind == nkCall:
44-
return (c[0][0].strVal, c[0][1].strVal)
45-
elif c[0].kind == nkIdent:
46-
return ($c[0], "id")
46+
if c[0].kind == nkColumnReference:
47+
return (sqlIdentName(c[0][0]), sqlIdentBaseName(c[0][1]))
48+
elif c[0].kind == nkCall:
49+
return (sqlIdentName(c[0][0]), sqlIdentBaseName(c[0][1]))
50+
elif c[0].kind in {nkIdent, nkQuotedIdent, nkDot}:
51+
return (sqlIdentName(c[0]), "id")
4752
("", "")
4853

49-
proc getType(n: SqlNode): DbType =
54+
proc collectEnumTypes(schemaSql: string): KnownEnums =
55+
result = initTable[string, seq[string]]()
56+
let definitions = parseEnumTypeDefs(schemaSql)
57+
for i in 0 ..< definitions.len:
58+
let definition = definitions[i]
59+
let typeName = sqlIdentName(definition[0]).toLowerAscii()
60+
var values: seq[string] = @[]
61+
for value in definition[1].sons:
62+
values.add(value.strVal)
63+
result[typeName] = values
64+
65+
let dotPos = typeName.rfind('.')
66+
if dotPos >= 0 and dotPos + 1 < typeName.len:
67+
result[typeName[dotPos + 1 .. ^1]] = values
68+
69+
proc getType(n: SqlNode; enums: KnownEnums): DbType =
5070
var it = n
5171
if it.kind == nkCall:
5272
it = it[0]
@@ -56,21 +76,28 @@ proc getType(n: SqlNode): DbType =
5676
for i in 0 ..< it.len:
5777
assert it[i].kind == nkStringLit
5878
result.validValues.add it[i].strVal
59-
elif it.kind in {nkIdent, nkStringLit}:
60-
result.kind = dbTypFromName(it.strVal)
61-
result.name = it.strVal
79+
elif it.kind in {nkIdent, nkQuotedIdent, nkStringLit, nkDot}:
80+
let typeName = sqlIdentName(it)
81+
let normalized = typeName.toLowerAscii
82+
if enums.hasKey(normalized):
83+
result.kind = dbEnum
84+
result.validValues = enums[normalized]
85+
else:
86+
let baseName = sqlIdentBaseName(it)
87+
result.kind = dbTypFromName(baseName)
88+
result.name = typeName
6289

63-
proc collectTables*(n: SqlNode; t: var KnownTables) =
90+
proc collectTables*(n: SqlNode; t: var KnownTables; enums: KnownEnums) =
6491
if n.isNil:
6592
return
6693
case n.kind
6794
of nkCreateTable, nkCreateTableIfNotExists:
68-
let tableName = n[0].strVal
95+
let tableName = sqlIdentName(n[0])
6996
var cols: DbColumns = @[]
7097
for i in 1 ..< n.len:
7198
let it = n[i]
7299
if it.kind == nkColumnDef:
73-
var typ = getType(it[1])
100+
var typ = getType(it[1], enums)
74101
if hasAttribute(it, {nkNotNull}):
75102
typ.notNull = true
76103
cols.add DbColumn(
@@ -97,9 +124,9 @@ proc collectTables*(n: SqlNode; t: var KnownTables) =
97124
var refTable = ""
98125
var refCols: seq[string] = @[]
99126
if r.kind == nkColumnReference or r.kind == nkCall:
100-
refTable = r[0].strVal
127+
refTable = sqlIdentName(r[0])
101128
for k in 1 ..< r.len:
102-
refCols.add(r[k].strVal)
129+
refCols.add(sqlIdentBaseName(r[k]))
103130
let pairCount = min(localCols.len, refCols.len)
104131
for k in 0 ..< pairCount:
105132
let localName = localCols[k]
@@ -111,25 +138,54 @@ proc collectTables*(n: SqlNode; t: var KnownTables) =
111138
t[tableName] = cols
112139
else:
113140
for i in 0 ..< n.len:
114-
collectTables(n[i], t)
141+
collectTables(n[i], t, enums)
115142

116143
proc attrToKey(a: DbColumn; t: KnownTables): int =
117144
if a.primaryKey:
118145
return 1
119146
if a.refs[0].len > 0:
147+
var referencedTable = ""
148+
for tableName in keys(t):
149+
if cmpIgnoreCase(tableName, a.refs[0]) == 0:
150+
referencedTable = tableName
151+
break
152+
153+
if referencedTable.len == 0 and '.' notin a.refs[0]:
154+
for tableName in keys(t):
155+
let dotPos = tableName.rfind('.')
156+
let baseName =
157+
if dotPos >= 0: tableName[dotPos + 1 .. ^1]
158+
else: tableName
159+
if cmpIgnoreCase(baseName, a.refs[0]) == 0:
160+
if referencedTable.len > 0:
161+
return 0
162+
referencedTable = tableName
163+
164+
if referencedTable.len == 0:
165+
return 0
166+
120167
var i = 0
121168
for k, v in pairs(t):
122169
for b in v:
123-
if cmpIgnoreCase(k, a.refs[0]) == 0 and cmpIgnoreCase(b.name, a.refs[1]) == 0:
170+
if cmpIgnoreCase(k, referencedTable) == 0 and cmpIgnoreCase(b.name, a.refs[1]) == 0:
124171
return -i - 1
125172
inc i
126173
0
127174

175+
proc addStringSeq(dest: var string; values: openArray[string]) =
176+
dest.add "@["
177+
for i, value in values:
178+
if i > 0:
179+
dest.add ", "
180+
dest.add escape(value)
181+
dest.add "]"
182+
128183
proc renderModelCode(schemaSql, schemaPath: string; target: ImportTarget; includeStatic = false): string =
129184
discard target
130185
let sql = parseSql(schemaSql, schemaPath)
186+
let enums = collectEnumTypes(schemaSql)
131187
var knownTables = initOrderedTable[string, DbColumns]()
132-
collectTables(sql, knownTables)
188+
collectTables(sql, knownTables, enums)
133189

134190
result.add FileHeader
135191
result.add "const tableNames = ["
@@ -158,6 +214,10 @@ proc renderModelCode(schemaSql, schemaPath: string; target: ImportTarget; includ
158214
result.add $i
159215
result.add ", typ: "
160216
result.add $a.typ.kind
217+
result.add ", typeName: "
218+
result.add escape(a.typ.name)
219+
result.add ", validValues: "
220+
result.addStringSeq(a.typ.validValues)
161221
result.add ", key: "
162222
result.add $attrToKey(a, knownTables)
163223
result.add ")"

0 commit comments

Comments
 (0)