Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion project.clj
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
:url "https://github.com/krisajenkins/yesql"}
:profiles {:dev {:dependencies [[expectations "2.1.3" :exclusions [org.clojure/clojure]]
[org.apache.derby/derby "10.12.1.1"]]
:plugins [[lein-autoexpect "1.4.0"]
:plugins [[lein-autoexpect "1.4.0" :exclusions [org.clojure/tools.namespace]]
[lein-expectations "0.0.8" :exclusions [org.clojure/clojure]]]}
:1.5 {:dependencies [[org.clojure/clojure "1.5.1"]]}
:1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]}
Expand Down
65 changes: 39 additions & 26 deletions src/yesql/generate.clj
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
[yesql.util :refer [create-root-var]]
[yesql.types :refer [map->Query]]
[yesql.statement-parser :refer [tokenize]])
(:import [yesql.types Query]))
(:import [yesql.types Query])
(import java.lang.IllegalArgumentException))

(def in-list-parameter?
"Check if a type triggers IN-list expansion."
Expand Down Expand Up @@ -35,37 +36,47 @@
(defn rewrite-query-for-jdbc
[tokens initial-args]
(let [{:keys [expected-keys expected-positional-count]} (analyse-statement-tokens tokens)
actual-keys (set (keys (dissoc initial-args :?)))
actual-keys (set (keys (dissoc (if (or (vector? initial-args) (list? initial-args)) (apply merge initial-args) initial-args) :?)))
actual-positional-count (count (:? initial-args))
missing-keys (set/difference expected-keys actual-keys)]
(assert (empty? missing-keys)
(format "Query argument mismatch.\nExpected keys: %s\nActual keys: %s\nMissing keys: %s"
(if-not (empty? missing-keys)
(throw (IllegalArgumentException. (format "Query argument mismatch.\nExpected keys: %s\nActual keys: %s\nMissing keys: %s"
(str (seq expected-keys))
(str (seq actual-keys))
(str (seq missing-keys))))
(assert (= expected-positional-count actual-positional-count)
(format (join "\n"
(str (seq missing-keys))))))
(if-not (= expected-positional-count actual-positional-count)
(throw (IllegalArgumentException. (format (join "\n"
["Query argument mismatch."
"Expected %d positional parameters. Got %d."
"Supply positional parameters as {:? [...]}"])
expected-positional-count actual-positional-count))
(let [[final-query final-parameters consumed-args]
(reduce (fn [[query parameters args] token]
(cond
(string? token) [(str query token)
parameters
args]
(symbol? token) (let [[arg new-args] (if (= '? token)
[(first (:? args)) (update-in args [:?] rest)]
[(get args (keyword token)) args])]
[(str query (args-to-placeholders arg))
(vec (if (in-list-parameter? arg)
(concat parameters arg)
(conj parameters arg)))
new-args])))
["" [] initial-args]
tokens)]
(concat [final-query] final-parameters))))
expected-positional-count actual-positional-count))))
(if (or (vector? initial-args) (list? initial-args))
(let [[final-query final-parameters consumed-args]
(reduce (fn [[query parameters args] token]
(cond
(string? token) [(str query token)
parameters
args]
(symbol? token) [(str query (args-to-placeholders ""))
(conj parameters (keyword token))
args])) ["" [] initial-args] tokens)] (concat [final-query] (mapv (apply juxt final-parameters) initial-args)))
(let [[final-query final-parameters consumed-args]
(reduce (fn [[query parameters args] token]
(cond
(string? token) [(str query token)
parameters
args]
(symbol? token) (let [[arg new-args] (if (= '? token)
[(first (:? args)) (update-in args [:?] rest)]
[(get args (keyword token)) args])]
[(str query (args-to-placeholders arg))
(vec (if (in-list-parameter? arg)
(concat parameters arg)
(conj parameters arg)))
new-args])))
["" [] initial-args]
tokens)]
(concat [final-query] final-parameters)))))

;; Maintainer's note: clojure.java.jdbc.execute! returns a list of
;; rowcounts, because it takes a list of parameter groups. In our
Expand All @@ -77,7 +88,9 @@

(defn insert-handler
[db statement-and-params call-options]
(jdbc/db-do-prepared-return-keys db statement-and-params))
(if (vector? (second statement-and-params))
(apply jdbc/db-do-prepared db statement-and-params)
(jdbc/db-do-prepared-return-keys db statement-and-params)))

(defn query-handler
[db sql-and-params
Expand Down
9 changes: 5 additions & 4 deletions test/yesql/generate_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
(:require [expectations :refer :all]
[clojure.template :refer [do-template]]
[yesql.statement-parser :refer [tokenize]]
[yesql.generate :refer :all]))
[yesql.generate :refer :all])
(:import [java.lang IllegalArgumentException]))

(do-template [statement _ expected-parameters]
(expect expected-parameters
Expand Down Expand Up @@ -82,13 +83,13 @@
"SELECT * FROM users WHERE group_ids IN(:group_ids) AND parent_id = :parent_id"
{:group_ids [1 2]
:parent_id 3}
=> ["SELECT * FROM users WHERE group_ids IN(?,?) AND parent_id = ?" 1 2 3])
=> ["SELECT * FROM users WHERE group_ids IN(?,?) AND parent_id = ?" 1 2 3])

;;; Incorrect parameters.
(expect AssertionError
(expect IllegalArgumentException
(rewrite-query-for-jdbc (tokenize "SELECT age FROM users WHERE country = :country AND name = :name")
{:country "gb"}))

(expect AssertionError
(expect IllegalArgumentException
(rewrite-query-for-jdbc (tokenize "SELECT age FROM users WHERE country = ? AND name = ?")
{}))