Skip to content

Commit 6b5019f

Browse files
committed
Query - Cleanup + suite tests
Signed-off-by: George Lemon <georgelemon@protonmail.com>
1 parent e97e992 commit 6b5019f

File tree

2 files changed

+177
-192
lines changed

2 files changed

+177
-192
lines changed

src/ozark/query.nim

Lines changed: 84 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -68,169 +68,141 @@ macro selectAll*(tableName: untyped): untyped =
6868
checkTableExists($tableName)
6969
result = newCall(bindSym"ozarkSelectResult", newLit("SELECT * FROM " & $tableName))
7070

71-
macro where*(sql: untyped, col: static string, val: untyped): untyped =
72-
## Define WHERE clause
71+
72+
#
73+
# WHERE clause macros
74+
#
75+
76+
# - WHERE caluse Writers
77+
proc writeWhereLikeStatements(op: static string, sql: NimNode,
78+
infix: NimNode, col: string): NimNode {.compileTime.} =
79+
# Writer macro for both `whereLike` and `whereNotLike` to avoid code duplication.
80+
# This macro generates the SQL string for the WHERE LIKE/NOT LIKE clause and
81+
# also constructs the appropriate infix expression for the value with wildcards
7382
if sql.kind != nnkCall or sql[0].strVal != "ozarkSelectResult":
74-
error("The first argument to `where` must be the result of a `select` macro.")
83+
error("The first argument to `where` statement must be the result of a `select` macro.")
7584
if col.validIdentifier:
7685
# todo check if column exists in model
7786
discard
7887
else:
7988
raise newException(OzarkModelDefect, "Invalid column name `" & col & "`")
8089
let selectSql = sql[1].strVal
8190
result = newCall(bindSym"ozarkWhereResult",
82-
newLit(selectSql & " WHERE " & col & " = $1"),
83-
val
91+
newLit(selectSql & " WHERE " & col & " " & op & " $1"),
92+
infix
8493
)
8594

86-
macro whereNot*(sql: untyped, col: static string, val: untyped): untyped =
87-
## Define WHERE clause with NOT
95+
proc writeWhereInWhereNotIn(op: static string,
96+
sql: NimNode, col: string, vals: NimNode): NimNode {.compileTime.} =
97+
# Writer macro for both `whereIn` and `whereNotIn` to avoid code duplication.
98+
# This macro generates the SQL string for the WHERE IN/NOT IN clause and
99+
# also adds the values as additional arguments to the macro result for later use in code generation
88100
if sql.kind != nnkCall or sql[0].strVal != "ozarkSelectResult":
89-
error("The first argument to `whereNot` must be the result of a `select` macro.")
101+
error("The first argument to must be the result of a `select` macro.")
90102
if col.validIdentifier:
91103
# todo check if column exists in model
92104
discard
93105
else:
94106
raise newException(OzarkModelDefect, "Invalid column name `" & col & "`")
95107
let selectSql = sql[1].strVal
96-
result = newCall(bindSym"ozarkWhereResult",
97-
newLit(selectSql & " WHERE " & col & " != $1"),
98-
val
108+
var placeholders = newSeq[string](vals.len)
109+
for i in 0..<vals.len:
110+
placeholders[i] = "$" & $(i + 1)
111+
result = newCall(
112+
bindSym"ozarkWhereInResult",
113+
newLit(selectSql & " WHERE " & col & " " & op & " (" & placeholders.join(",") & ")"),
99114
)
115+
for i in 0..<vals.len:
116+
# add the values as additional arguments to the
117+
# macro result for later use in code generation
118+
result.add(vals[i])
100119

101-
macro orWhere*(sql: untyped, col: static string, val: untyped): untyped =
102-
## Define OR in WHERE clause
103-
if sql.kind != nnkCall or sql[0].strVal != "ozarkWhereResult":
104-
error("The first argument to `orWhere` must be the result of a `where` macro.")
120+
proc writeWhereStatement(op: static string, sql: NimNode, col: string, val: NimNode): NimNode {.compileTime.} =
121+
# Writer macro for simple WHERE clauses (e.g. `where`, `whereNot`) to avoid code duplication.
122+
if sql.kind != nnkCall or sql[0].strVal != "ozarkSelectResult":
123+
error("The first argument to `where` must be the result of a `select` macro.")
105124
if col.validIdentifier:
106125
# todo check if column exists in model
107126
discard
108127
else:
109128
raise newException(OzarkModelDefect, "Invalid column name `" & col & "`")
110-
let whereSql = sql[1].strVal
129+
let selectSql = sql[1].strVal
111130
result = newCall(bindSym"ozarkWhereResult",
112-
newLit(whereSql & " OR " & col & " = $1"),
131+
newLit(selectSql & " WHERE " & col & " " & op & " $1"),
113132
val
114133
)
115134

116-
macro whereStartsLike*(sql: untyped, col: static string, val: untyped): untyped =
117-
## Define WHERE clause with LIKE for prefix matching
118-
if sql.kind != nnkCall or sql[0].strVal != "ozarkSelectResult":
119-
error("The first argument to `whereLike` must be the result of a `select` macro.")
135+
proc writeOrWhereStatement(op: static string, sql: NimNode, col: string, val: NimNode): NimNode {.compileTime.} =
136+
# Writer macro for `orWhere` to avoid code duplication with `writeWhereStatement`.
137+
# This macro checks that the first argument is a valid `where` result and then
138+
# appends the new condition with an OR to the existing SQL string.
139+
if sql.kind != nnkCall or sql[0].strVal != "ozarkWhereResult":
140+
error("The first argument to `orWhere` must be the result of a `where` macro.")
120141
if col.validIdentifier:
121142
# todo check if column exists in model
122143
discard
123144
else:
124145
raise newException(OzarkModelDefect, "Invalid column name `" & col & "`")
125-
let selectSql = sql[1].strVal
146+
let whereSql = sql[1].strVal
126147
result = newCall(bindSym"ozarkWhereResult",
127-
newLit(selectSql & " WHERE " & col & " LIKE $1"),
128-
nnkInfix.newTree(ident"&", val, newLit("%"))
148+
newLit(whereSql & " OR " & col & " " & op & " $1"),
149+
val
129150
)
130151

152+
# WHERE clause public macros
153+
macro where*(sql: untyped, col: static string, val: untyped): untyped =
154+
## Define WHERE clause
155+
writeWhereStatement("=", sql, col, val)
156+
157+
macro whereNot*(sql: untyped, col: static string, val: untyped): untyped =
158+
## Define WHERE clause with NOT
159+
writeWhereStatement("!=", sql, col, val)
160+
161+
macro orWhere*(sql: untyped, col: static string, val: untyped): untyped =
162+
## Define OR in WHERE clause
163+
writeOrWhereStatement("=", sql, col, val)
164+
165+
macro orWhereNot*(sql: untyped, col: static string, val: untyped): untyped =
166+
## Define OR with NOT in WHERE clause
167+
writeOrWhereStatement("!=", sql, col, val)
168+
169+
macro whereStartsLike*(sql: untyped, col: static string, val: untyped): untyped =
170+
## Define WHERE clause with LIKE for prefix matching
171+
writeWhereLikeStatements("LIKE", sql,
172+
nnkInfix.newTree(ident"&", val, newLit("%")), col)
173+
131174
macro whereEndsLike*(sql: untyped, col: static string, val: untyped): untyped =
132175
## Define WHERE clause with LIKE for suffix matching
133-
if sql.kind != nnkCall or sql[0].strVal != "ozarkSelectResult":
134-
error("The first argument to `whereLike` must be the result of a `select` macro.")
135-
if col.validIdentifier:
136-
# todo check if column exists in model
137-
discard
138-
else:
139-
raise newException(OzarkModelDefect, "Invalid column name `" & col & "`")
140-
let selectSql = sql[1].strVal
141-
result = newCall(bindSym"ozarkWhereResult",
142-
newLit(selectSql & " WHERE " & col & " LIKE $1"),
143-
nnkInfix.newTree(ident"&", newLit("%"), val)
144-
)
176+
writeWhereLikeStatements("LIKE", sql,
177+
nnkInfix.newTree(ident"&", newLit("%"), val), col)
145178

146179
macro whereLike*(sql: untyped, col: static string, val: untyped): untyped =
147180
## Define WHERE clause with LIKE for any position
148-
if sql.kind != nnkCall or sql[0].strVal != "ozarkSelectResult":
149-
error("The first argument to `whereLikeAny` must be the result of a `select` macro.")
150-
if col.validIdentifier:
151-
# todo check if column exists in model
152-
discard
153-
else:
154-
raise newException(OzarkModelDefect, "Invalid column name `" & col & "`")
155-
let selectSql = sql[1].strVal
156-
result = newCall(bindSym"ozarkWhereResult",
157-
newLit(selectSql & " WHERE " & col & " LIKE $1"),
158-
nnkInfix.newTree(
159-
ident"&",
160-
val,
161-
newLit("%")
162-
)
163-
)
181+
writeWhereLikeStatements("LIKE", sql,
182+
nnkInfix.newTree(ident"&", val, newLit("%")), col)
164183

165184
macro whereNotLike*(sql: untyped, col: static string, val: untyped): untyped =
166185
## Define WHERE clause with NOT LIKE for any position
167-
if sql.kind != nnkCall or sql[0].strVal != "ozarkSelectResult":
168-
error("The first argument to `whereNotLike` must be the result of a `select` macro.")
169-
if col.validIdentifier:
170-
# todo check if column exists in model
171-
discard
172-
else:
173-
raise newException(OzarkModelDefect, "Invalid column name `" & col & "`")
174-
let selectSql = sql[1].strVal
175-
result = newCall(bindSym"ozarkWhereResult",
176-
newLit(selectSql & " WHERE " & col & " NOT LIKE $1;"),
177-
nnkInfix.newTree(
178-
ident"&",
179-
val,
180-
newLit("%")
181-
)
182-
)
186+
writeWhereLikeStatements("NOT LIKE", sql,
187+
nnkInfix.newTree(ident"&", val, newLit("%")), col)
183188

184189
macro whereNotStartsLike*(sql: untyped, col: static string, val: untyped): untyped =
185-
## Define WHERE clause with NOT LIKE for prefix matching
186-
if sql.kind != nnkCall or sql[0].strVal != "ozarkSelectResult":
187-
error("The first argument to `whereNotStartsLike` must be the result of a `select` macro.")
188-
if col.validIdentifier:
189-
# todo check if column exists in model
190-
discard
191-
else:
192-
raise newException(OzarkModelDefect, "Invalid column name `" & col & "`")
193-
let selectSql = sql[1].strVal
194-
result = newCall(bindSym"ozarkWhereResult",
195-
newLit(selectSql & " WHERE " & col & " NOT LIKE $1;"),
196-
nnkInfix.newTree(ident"&", val, newLit("%"))
197-
)
190+
## Define WHERE clause with `NOT LIKE` for prefix matching
191+
writeWhereLikeStatements("NOT LIKE", sql,
192+
nnkInfix.newTree(ident"&", val, newLit("%")), col)
198193

199194
macro whereNotEndsLike*(sql: untyped, col: static string, val: untyped): untyped =
200-
## Define WHERE clause with NOT LIKE for suffix matching
201-
if sql.kind != nnkCall or sql[0].strVal != "ozarkSelectResult":
202-
error("The first argument to `whereNotEndsLike` must be the result of a `select` macro.")
203-
if col.validIdentifier:
204-
# todo check if column exists in model
205-
discard
206-
else:
207-
raise newException(OzarkModelDefect, "Invalid column name `" & col & "`")
208-
let selectSql = sql[1].strVal
209-
result = newCall(bindSym"ozarkWhereResult",
210-
newLit(selectSql & " WHERE " & col & " NOT LIKE $1;"),
211-
nnkInfix.newTree(ident"&", newLit("%"), val)
212-
)
195+
## Define WHERE clause with `NOT LIKE` for suffix matching
196+
writeWhereLikeStatements("NOT LIKE", sql,
197+
nnkInfix.newTree(ident"&", newLit("%"), val), col)
213198

214199
macro whereIn*(sql: untyped, col: static string, vals: openArray[untyped]): untyped =
215200
## Define WHERE clause with IN operator
216-
if sql.kind != nnkCall or sql[0].strVal != "ozarkSelectResult":
217-
error("The first argument to `whereIn` must be the result of a `select` macro.")
218-
if col.validIdentifier:
219-
# todo check if column exists in model
220-
discard
221-
else:
222-
raise newException(OzarkModelDefect, "Invalid column name `" & col & "`")
223-
let selectSql = sql[1].strVal
224-
var placeholders = newSeq[string](vals.len)
225-
for i in 0..<vals.len:
226-
placeholders[i] = "$" & $(i + 1)
227-
result = newCall(
228-
bindSym"ozarkWhereInResult",
229-
newLit(selectSql & " WHERE " & col & " IN (" & placeholders.join(",") & ")"),
230-
)
231-
for i in 0..<vals.len:
232-
# add the values as additional arguments to the macro result for later use in code generation
233-
result.add(vals[i])
201+
writeWhereInWhereNotIn("IN", sql, col, vals)
202+
203+
macro whereNotIn*(sql: untyped, col: static string, vals: openArray[untyped]): untyped =
204+
## Define WHERE clause with NOT IN operator
205+
writeWhereInWhereNotIn("NOT IN", sql, col, vals)
234206

235207
template parseSqlQuery(getRowProcName: string, args: seq[NimNode] = @[]) {.dirty.} =
236208
try:

0 commit comments

Comments
 (0)