diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f93893..50d19c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Changes * 2.2.next in progress + * Address #377 by adding `honey.sql/map=` to convert a hash map into an equality condition (for a `WHERE` clause). * Address #281 by adding support for `SELECT * EXCEPT ..` and `SELECT * REPLACE ..` and `ARRAY<>` and `STRUCT<>` column types -- see [SQL Clause Reference - SELECT](https://cljdoc.org/d/com.github.seancorfield/honeysql/CURRENT/doc/getting-started/sql-clause-reference#select-select-distinct) and [SQL Clause Reference - DDL](https://cljdoc.org/d/com.github.seancorfield/honeysql/CURRENT/doc/getting-started/sql-clause-reference#ddl-clauses) respectively for more details. * Update `build-clj` to v0.6.7. diff --git a/doc/clause-reference.md b/doc/clause-reference.md index b598d64..845001e 100644 --- a/doc/clause-reference.md +++ b/doc/clause-reference.md @@ -674,6 +674,17 @@ The `:where` clause can have a single SQL expression, or a sequence of SQL expressions prefixed by either `:and` or `:or`. See examples of `:where` in various clauses above. +Sometimes it is convenient to construct a `WHERE` clause that +tests several columns for equality, and you might have a Clojure +hash map containing those values. `honey.sql/map=` exists to +convert a hash map of values into a condition that you can use +in a `WHERE` clause to match against those columns and values: + +```clojure +user=> (sql/format {:select :* :from :transaction :where (sql/map= {:type "sale" :productid 123})}) +["SELECT * FROM transaction WHERE (type = ?) AND (productid = ?)" "sale" 123] +``` + ## group-by `:group-by` accepts a sequence of one or more SQL expressions. diff --git a/src/honey/sql.cljc b/src/honey/sql.cljc index e680632..b33d4c5 100644 --- a/src/honey/sql.cljc +++ b/src/honey/sql.cljc @@ -1484,6 +1484,24 @@ (when ignore-nil (swap! op-ignore-nil conj op)))) +;; helper functions to create HoneySQL data structures from other things + +(defn map= + "Given a hash map, return a condition structure that can be used in a + WHERE clause to test for equality: + + {:select :* :from :table :where (sql/map= {:id 1})} + + will produce: SELECT * FROM table WHERE id = ? (and a parameter of 1)" + [data] + (let [clauses (reduce-kv (fn [where col val] + (conj where [:= col val])) + [] + data)] + (if (= 1 (count clauses)) + (first clauses) + (into [:and] clauses)))) + ;; aids to migration from HoneySQL 1.x -- these are deliberately undocumented ;; so as not to encourage their use for folks starting fresh with 2.x! diff --git a/test/honey/sql_test.cljc b/test/honey/sql_test.cljc index ab8c538..7dbc51b 100644 --- a/test/honey/sql_test.cljc +++ b/test/honey/sql_test.cljc @@ -75,6 +75,8 @@ (deftest general-tests (is (= ["SELECT * FROM \"table\" WHERE \"id\" = ?" 1] (sut/format {:select [:*] :from [:table] :where [:= :id 1]} {:quoted true}))) + (is (= ["SELECT * FROM \"table\" WHERE \"id\" = ?" 1] + (sut/format {:select [:*] :from [:table] :where (sut/map= {:id 1})} {:quoted true}))) (is (= ["SELECT \"t\".* FROM \"table\" AS \"t\" WHERE \"id\" = ?" 1] (sut/format {:select [:t.*] :from [[:table :t]] :where [:= :id 1]} {:quoted true}))) (is (= ["SELECT * FROM \"table\" GROUP BY \"foo\", \"bar\""]