make numbered params work with 'in'

This commit is contained in:
Sean Corfield 2022-12-16 23:56:53 -08:00
parent 4ea630ed90
commit c62f5da3f8
2 changed files with 39 additions and 18 deletions

View file

@ -330,7 +330,7 @@
(defn ->numbered-param [k] (defn ->numbered-param [k]
(let [n (count (swap! *numbered* conj k))] (let [n (count (swap! *numbered* conj k))]
[(str "$" n) (with-meta (constantly k) [(str "$" n) (with-meta (constantly (dec n))
{::wrapper {::wrapper
(fn [fk _] (param-value (get @*numbered* (fk))))})])) (fn [fk _] (param-value (get @*numbered* (fk))))})]))
@ -1295,33 +1295,44 @@
(defn- format-in [in [x y]] (defn- format-in [in [x y]]
(let [[sql-x & params-x] (format-expr x {:nested true}) (let [[sql-x & params-x] (format-expr x {:nested true})
[sql-y & params-y] (format-expr y {:nested true}) [sql-y & params-y] (format-expr y {:nested true})
values (unwrap (first params-y) {})] [v1 :as values] (map #(unwrap % {}) params-y)]
;; #396: prevent caching IN () when named parameter is used: ;; #396: prevent caching IN () when named parameter is used:
(when (and (meta (first params-y)) (when (and (meta (first params-y))
(::wrapper (meta (first params-y))) (::wrapper (meta (first params-y)))
*caching*) *caching*)
(throw (ex-info "SQL that includes IN () expressions cannot be cached" {}))) (throw (ex-info "SQL that includes IN () expressions cannot be cached" {})))
(when-not (= :none *checking*) (when-not (= :none *checking*)
(when (or (and (sequential? y) (empty? y)) (when (or (and (sequential? y) (empty? y))
(and (sequential? values) (empty? values))) (and (sequential? v1) (empty? v1)))
(throw (ex-info "IN () empty collection is illegal" (throw (ex-info "IN () empty collection is illegal"
{:clause [in x y]}))) {:clause [in x y]})))
(when (and (= :strict *checking*) (when (and (= :strict *checking*)
(or (and (sequential? y) (some nil? y)) (or (and (sequential? y) (some nil? y))
(and (sequential? values) (some nil? values)))) (and (sequential? v1) (some nil? v1))))
(throw (ex-info "IN (NULL) does not match" (throw (ex-info "IN (NULL) does not match"
{:clause [in x y]})))) {:clause [in x y]}))))
;; TODO: numbered params!?!? (cond (and (not *numbered*)
(if (and (= "?" sql-y) (= "?" sql-y)
(= 1 (count params-y)) (= 1 (count params-y))
(coll? values)) (coll? v1))
(let [sql (str "(" (str/join ", " (repeat (count values) "?")) ")")] (let [sql (str "(" (str/join ", " (repeat (count v1) "?")) ")")]
(-> [(str sql-x " " (sql-kw in) " " sql)] (-> [(str sql-x " " (sql-kw in) " " sql)]
(into params-x) (into params-x)
(into values))) (into v1)))
(-> [(str sql-x " " (sql-kw in) " " sql-y)] (and *numbered*
(into params-x) (= (str "$" (count @*numbered*)) sql-y)
(into params-y))))) (= 1 (count params-y))
(coll? v1))
(let [vs (for [v v1] (->numbered v))
sql (str "(" (str/join ", " (map first vs)) ")")]
(-> [(str sql-x " " (sql-kw in) " " sql)]
(into params-x)
(conj nil)
(into (map second vs))))
:else
(-> [(str sql-x " " (sql-kw in) " " sql-y)]
(into params-x)
(into (if *numbered* values params-y))))))
(defn- function-0 [k xs] (defn- function-0 [k xs]
[(str (sql-kw k) [(str (sql-kw k)

View file

@ -322,7 +322,17 @@
(sql/format {:select [:*] (sql/format {:select [:*]
:from [:customers] :from [:customers]
:where [:in :id :?ids]} :where [:in :id :?ids]}
{:params {:ids values}}))))))) {:params {:ids values}})))
(is (= ["SELECT * FROM customers WHERE id IN ($1, $2)" "1" "2"]
(sql/format {:select [:*]
:from [:customers]
:where [:in :id values]}
{:numbered true})))
(is (= ["SELECT * FROM customers WHERE id IN ($2, $3)" nil "1" "2"]
(sql/format {:select [:*]
:from [:customers]
:where [:in :id :?ids]}
{:params {:ids values} :numbered true})))))))
(deftest test-case (deftest test-case
(is (= ["SELECT CASE WHEN foo < ? THEN ? WHEN (foo > ?) AND ((foo MOD ?) = ?) THEN foo / ? ELSE ? END FROM bar" (is (= ["SELECT CASE WHEN foo < ? THEN ? WHEN (foo > ?) AND ((foo MOD ?) = ?) THEN foo / ? ELSE ? END FROM bar"