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
Allowed choosing the IN-list data structure type
  • Loading branch information
avalente committed Feb 17, 2016
commit 72db1f551ced1ebf692cf7f6b9a1da1e29b82185
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,26 @@ The query will be automatically expanded to `... IN (1001, 1003, 1005)
Just remember that some databases have a limit on the number of values
in an `IN`-list, and Yesql makes no effort to circumvent such limits.

By default, vectors, lists and seqs are automatically expanded as explained
above, but this behaviour can be overridden by passing an extra parameter
to the query creation functions (`defquery`, `defqueries` and `require-sql`):

```clojure
(defqueries "some/where/queryfile.sql"
{:connection db-spec
:in-list-parameter-predicate set?})

(find-users {:id #{1001 1003 1005}
:maxage 18})
```

In this way you can use data structures such as vectors for other purposes,
for example you are free to use `PostgreSQL`'s ARRAYs.
The `:in-list-parameter-predicate` must be a predicate used to identify
the data structure that must be expanded, but keep in mind that the choosen
data structure must respond to the `Seq` interface. A `nil` value selects
the default behaviour.

### Row And Result Processors

Like `clojure.java.jdbc`, Yesql accepts functions to pre-process each
Expand Down
17 changes: 11 additions & 6 deletions src/yesql/generate.clj
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
[yesql.statement-parser :refer [tokenize]])
(:import [yesql.types Query]))

(def in-list-parameter?
(def default-in-list-parameter?
"Check if a type triggers IN-list expansion."
(some-fn list? vector? seq?))

(defn- args-to-placeholders
[args]
[args in-list-parameter?]
(if (in-list-parameter? args)
(clojure.string/join "," (repeat (count args) "?"))
"?"))
Expand All @@ -33,7 +33,10 @@
(conj expected-keys :?))))

(defn rewrite-query-for-jdbc
[tokens initial-args]
([tokens initial-args]
(rewrite-query-for-jdbc tokens initial-args default-in-list-parameter?))

([tokens initial-args in-list-parameter?]
(let [{:keys [expected-keys expected-positional-count]} (analyse-statement-tokens tokens)
actual-keys (set (keys (dissoc initial-args :?)))
actual-positional-count (count (:? initial-args))
Expand All @@ -58,14 +61,14 @@
(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))
[(str query (args-to-placeholders arg in-list-parameter?))
(vec (if (in-list-parameter? arg)
(concat parameters arg)
(conj parameters arg)))
new-args])))
["" [] initial-args]
tokens)]
(concat [final-query] final-parameters))))
(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 Down Expand Up @@ -109,6 +112,8 @@
required-args (expected-parameter-list statement)
global-connection (:connection query-options)
tokens (tokenize statement)
in-list-p (:in-list-parameter-predicate query-options)
in-list-parameter? (if (nil? in-list-p) default-in-list-parameter? in-list-p)
real-fn (fn [args call-options]
(let [connection (or (:connection call-options)
global-connection)]
Expand All @@ -118,7 +123,7 @@
"Check the docs, and supply {:connection ...} as an option to the function call, or globally to the defquery declaration."])
name))
(jdbc-fn connection
(rewrite-query-for-jdbc tokens args)
(rewrite-query-for-jdbc tokens args in-list-parameter?)
call-options)))
[display-args generated-function] (let [named-args (if-let [as-vec (seq (mapv (comp symbol clojure.core/name)
required-args))]
Expand Down
1 change: 1 addition & 0 deletions test/yesql/acceptance_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,4 @@

(expect SQLSyntaxErrorException
(syntax-error))

31 changes: 31 additions & 0 deletions test/yesql/acceptance_test_custom_in_list.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
(ns yesql.acceptance-test-custom-in-list
(:require [expectations :refer :all]
[clojure.java.jdbc :as jdbc]
[yesql.core :refer :all])
(:import [java.sql SQLException SQLSyntaxErrorException SQLDataException]))

(def derby-db {:subprotocol "derby"
:subname (gensym "memory:")
:create true})

;;; Multiple-query workflow.
(defqueries
"yesql/sample_files/acceptance_test_combined.sql"
{:connection derby-db :in-list-parameter-predicate set?})

;; Create
(expect (create-person-table!))

;; Insert -> Select.
(expect {:1 1M} (insert-person<! {:name "Alice"
:age 20}))
(expect {:1 2M} (insert-person<! {:name "Bob"
:age 25}))
(expect {:1 3M} (insert-person<! {:name "Charlie"
:age 35}))

;;; Select with IN.
(expect 2 (count (find-by-age {:age #{20 35}})))

;; Drop
(expect (drop-person-table!))
2 changes: 1 addition & 1 deletion test/yesql/generate_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

;;; Testing in-list-parmaeter for "IN-list" statements.
(expect [true true true false false]
(map in-list-parameter?
(map default-in-list-parameter?
(list []
(list)
(lazy-seq (cons 1 [2]))
Expand Down