From 2820662f59d58b48ad1528d7f19b4875377d3441 Mon Sep 17 00:00:00 2001 From: Donald Ball Date: Mon, 20 Apr 2015 22:49:33 -0400 Subject: [PATCH 1/3] Apply seq to sets when converting to sql This allows them to be used as values, e.g. for in clauses, as demonstrated in the test. --- CHANGES.md | 1 + src/honeysql/format.clj | 3 +++ test/honeysql/core_test.clj | 10 ++++++++++ 3 files changed, 14 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 5d56d54..08fb39f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,6 @@ ## 0.5.3 In development +* Apply seq to sets when converting to sql (@dball) * Support locking selects (@dball) * Add sql array type and reader literal (@loganmhb) diff --git a/src/honeysql/format.clj b/src/honeysql/format.clj index 192e22b..4687013 100644 --- a/src/honeysql/format.clj +++ b/src/honeysql/format.clj @@ -320,6 +320,9 @@ (if *subquery?* (paren-wrap sql-str) sql-str))) + clojure.lang.IPersistentSet + (-to-sql [x] + (-to-sql (seq x))) nil (-to-sql [x] "NULL") SqlParam diff --git a/test/honeysql/core_test.clj b/test/honeysql/core_test.clj index b465411..235586e 100644 --- a/test/honeysql/core_test.clj +++ b/test/honeysql/core_test.clj @@ -80,3 +80,13 @@ (columns :bar) (values [[(honeysql.format/value {:baz "my-val"})]]) sql/format)))) + +(deftest test-operators + (testing "in" + (doseq [[cname coll] [[:vector []] [:set #{}] [:list '()]]] + (testing (str "with values from a " (name cname)) + (let [values (conj coll 1)] + (is (= ["SELECT * FROM customers WHERE (id in (1))"] + (sql/format {:select [:*] + :from [:customers] + :where [:in :id values]})))))))) From 4bdaae39afc02e8f92ff0c3e691eb13c81aac36a Mon Sep 17 00:00:00 2001 From: Donald Ball Date: Tue, 21 Apr 2015 14:28:28 -0400 Subject: [PATCH 2/3] Convert seq param values to literal sql param lists This allows seqs to be used as param values, e.g. for IN clauses. It also converts sets to seqs for the same purpose. It does not expand the parameter name when this occurs, which will lead to a mismatch between the *param-names* and *params* collections. It is not clear how *param-names* are intended to be used, so I have no better fix to suggest other than repeating a seq's param name the corresponding number of times into that collection. --- CHANGES.md | 1 + src/honeysql/format.clj | 18 ++++++++++++++++-- test/honeysql/core_test.clj | 28 +++++++++++++++++++++++++++- 3 files changed, 44 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 08fb39f..f61278e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,6 @@ ## 0.5.3 In development +* Convert seq param values to literal sql param lists (@dball) * Apply seq to sets when converting to sql (@dball) * Support locking selects (@dball) * Add sql array type and reader literal (@loganmhb) diff --git a/src/honeysql/format.clj b/src/honeysql/format.clj index 4687013..99f5f8b 100644 --- a/src/honeysql/format.clj +++ b/src/honeysql/format.clj @@ -248,10 +248,24 @@ (into [sql-str] @*params*) [sql-str])))) +(defprotocol Parameterizable + (to-params [value])) + +(extend-protocol Parameterizable + clojure.lang.Sequential + (to-params [value] + (paren-wrap (comma-join (mapv to-params value)))) + clojure.lang.IPersistentSet + (to-params [value] + (to-params (seq value))) + java.lang.Object + (to-params [value] + (swap! *params* conj value) + (*parameterizer*))) + (defn add-param [pname pval] (swap! *param-names* conj pname) - (swap! *params* conj pval) - (*parameterizer*)) + (to-params pval)) ;; Anonymous param name -- :_1, :_2, etc. (defn add-anon-param [pval] diff --git a/test/honeysql/core_test.clj b/test/honeysql/core_test.clj index 235586e..9545bb7 100644 --- a/test/honeysql/core_test.clj +++ b/test/honeysql/core_test.clj @@ -89,4 +89,30 @@ (is (= ["SELECT * FROM customers WHERE (id in (1))"] (sql/format {:select [:*] :from [:customers] - :where [:in :id values]})))))))) + :where [:in :id values]}))) + (is (= ["SELECT * FROM customers WHERE (id in (?))" 1] + (sql/format {:select [:*] + :from [:customers] + :where [:in :id :?ids]} + {:ids values})))))) + (testing "with more than one integer" + (let [values [1 2]] + (is (= ["SELECT * FROM customers WHERE (id in (1, 2))"] + (sql/format {:select [:*] + :from [:customers] + :where [:in :id values]}))) + (is (= ["SELECT * FROM customers WHERE (id in (?, ?))" 1 2] + (sql/format {:select [:*] + :from [:customers] + :where [:in :id :?ids]} + {:ids values}))))) + (testing "with more than one string" + (let [values ["1" "2"]] + (is (= ["SELECT * FROM customers WHERE (id in (?, ?))" "1" "2"] + (sql/format {:select [:*] + :from [:customers] + :where [:in :id values]}) + (sql/format {:select [:*] + :from [:customers] + :where [:in :id :?ids]} + {:ids values}))))))) From 6b64743315079a4147ecb93f5e61cd472c55a951 Mon Sep 17 00:00:00 2001 From: Mike Blume Date: Fri, 8 May 2015 14:20:24 -0700 Subject: [PATCH 3/3] add pname to *param-names* once per value keep *params* and *param-names* at the same count --- src/honeysql/format.clj | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/honeysql/format.clj b/src/honeysql/format.clj index 99f5f8b..0c04fe7 100644 --- a/src/honeysql/format.clj +++ b/src/honeysql/format.clj @@ -249,23 +249,23 @@ [sql-str])))) (defprotocol Parameterizable - (to-params [value])) + (to-params [value pname])) (extend-protocol Parameterizable clojure.lang.Sequential - (to-params [value] - (paren-wrap (comma-join (mapv to-params value)))) + (to-params [value pname] + (paren-wrap (comma-join (mapv #(to-params % pname) value)))) clojure.lang.IPersistentSet - (to-params [value] - (to-params (seq value))) + (to-params [value pname] + (to-params (seq value) pname)) java.lang.Object - (to-params [value] + (to-params [value pname] (swap! *params* conj value) + (swap! *param-names* conj pname) (*parameterizer*))) (defn add-param [pname pval] - (swap! *param-names* conj pname) - (to-params pval)) + (to-params pval pname)) ;; Anonymous param name -- :_1, :_2, etc. (defn add-anon-param [pval]