Hi,
Here are some options.
Consider the following example data:
(def xys (partition 2 (range 6)))
;; ((0 1) (2 3) (4 5))
1) Building a list and eval-ing it:
(dry-run (select :foo (where (eval (list* or (map #(zipmap [:x :y] %) xys))))))
;; dry run :: SELECT foo.* FROM foo WHERE ((foo.x = ? AND foo.y = ?) OR (foo.x = ? AND foo.y = ?) OR (foo.x = ? AND foo.y = ?)) :: [0 1 2 3 4 5]
Really hacky, I wouldn't put eval in my production code.
2) Use korma.sql.fns/pred-or instead of `or` macro
(dry-run (select :foo (where (apply korma.sql.fns/pred-or (map #(zipmap [:x :y] %) xys)))))
;; dry run :: SELECT foo.* FROM foo WHERE ((foo.x = ? AND foo.y = ?) OR (foo.x = ? AND foo.y = ?) OR (foo.x = ? AND foo.y = ?)) :: [0 1 2 3 4 5]
This is probably the cleanest solution, the only downside being that `pred-or` is kind of internals of Korma.
3) Building where string by yourself:
(dry-run (select :foo (where (clojure.string/join " OR " (map (fn [[x y]] (format "(x = %s AND y = %s)" x y)) xys)))))
;; dry run :: SELECT foo.* FROM foo WHERE (x = 0 AND y = 1) OR (x = 2 AND y = 3) OR (x = 4 AND y = 5) :: []
4) Union with subqueries:
(dry-run (exec (reduce queries (union*) (map (fn [[x y]] (subselect :foo (where {:x x :y y}))) xys))))
;;dry run :: (SELECT foo.* FROM foo WHERE (foo.x = ? AND foo.y = ?)) UNION (SELECT foo.* FROM foo WHERE (foo.x = ? AND foo.y = ?)) UNION (SELECT foo.* FROM foo WHERE (foo.x = ? AND foo.y = ?)) :: [0 1 2 3 4 5]
Not very readable in my opinion.
5) Generate the entire sql statement yourself and use exec-raw.
None of these options is really optimal and constructing sql dynamically is unnecessarily difficult. As you said, this is largely due to heavy use of macros in the implementation. I couldn't agree more with you, if sql was represented as regular Clojure datastructures, it would more easier to compose and manipulate queries. It is unlikely that Korma will take this direction, but I think there's plenty of room for new SQL libs in the ecosystem.
:: Immo