Fixes #308 by adding support for clauses

This also corrects the docstring for the join-by helper.
This commit is contained in:
Sean Corfield 2021-04-11 04:18:14 -07:00
parent d734767877
commit e6a5bdb001
5 changed files with 48 additions and 9 deletions

View file

@ -4,6 +4,7 @@
* The documentation continues to be expanded and clarified in response to feedback! * The documentation continues to be expanded and clarified in response to feedback!
* Tentative fix for #315 by expanding `:in` handling to deal with `nil` values. * Tentative fix for #315 by expanding `:in` handling to deal with `nil` values.
* Fix #310 by adding support for `FILTER`, `WITHIN GROUP`, and `ORDER BY` (as an expression), from [nilenso/honeysql-postgres](https://github.com/nilenso/honeysql-postgres) 0.4.112. These are [Special Syntax](doc/special-syntax.md) and there are also helpers for `filter` and `within-group` -- so **be careful about referring in all of `honey.sql.helpers`** since it will now shadow `clojure.core/filter` (it already shadows `for`, `group-by`, `into`, `partition-by`, `set`, and `update`). * Fix #310 by adding support for `FILTER`, `WITHIN GROUP`, and `ORDER BY` (as an expression), from [nilenso/honeysql-postgres](https://github.com/nilenso/honeysql-postgres) 0.4.112. These are [Special Syntax](doc/special-syntax.md) and there are also helpers for `filter` and `within-group` -- so **be careful about referring in all of `honey.sql.helpers`** since it will now shadow `clojure.core/filter` (it already shadows `for`, `group-by`, `into`, `partition-by`, `set`, and `update`).
* Fix #308 by supporting join clauses in `join-by`.
* 2.0.0-beta1 (for testing; 2021-04-09) * 2.0.0-beta1 (for testing; 2021-04-09)
* Since Alpha 3, more documentation has been written and existing documentation clarified (addressing #300, #309, #313, #314). * Since Alpha 3, more documentation has been written and existing documentation clarified (addressing #300, #309, #313, #314).

View file

@ -528,9 +528,9 @@ for more detail).
## join-by ## join-by
This is a convenience that allows for an arbitrary sequence of `JOIN` This is a convenience that allows for an arbitrary sequence of `JOIN`
operations to be performed in a specific order. It accepts a sequence operations to be performed in a specific order. It accepts either a sequence
of join operation name (keyword or symbol) and the clause that join of alternating join operation name (keyword or symbol) and the clause that join
would take: would take, or a sequence of `JOIN` clauses as hash maps:
```clojure ```clojure
user=> (sql/format {:select [:t.ref :pp.code] user=> (sql/format {:select [:t.ref :pp.code]
@ -540,7 +540,24 @@ user=> (sql/format {:select [:t.ref :pp.code]
:join [[:logtransaction :log] :join [[:logtransaction :log]
[:= :t.id :log.id]]] [:= :t.id :log.id]]]
:where [:= "settled" :pp.status]}) :where [:= "settled" :pp.status]})
["SELECT t.ref, pp.code FROM transaction AS t LEFT JOIN paypal_tx AS pp USING (id) INNER JOIN logtransaction AS log ON t.id = log.id WHERE ? = pp.status" "settled"] ;; newlines inserted for readability:
["SELECT t.ref, pp.code FROM transaction AS t
LEFT JOIN paypal_tx AS pp USING (id)
INNER JOIN logtransaction AS log ON t.id = log.id
WHERE ? = pp.status" "settled"]
;; or using helpers:
user=> (sql/format (-> (select :t.ref :pp.code)
(from [:transaction :t])
(join-by (left-join [:paypal-tx :pp]
[:using :id])
(join [:logtransaction :log]
[:= :t.id :log.id]))
(where := "settled" :pp.status)))
;; newlines inserted for readability:
["SELECT t.ref, pp.code FROM transaction AS t
LEFT JOIN paypal_tx AS pp USING (id)
INNER JOIN logtransaction AS log ON t.id = log.id
WHERE ? = pp.status" "settled"]
``` ```
Without `:join-by`, a `:join` would normally be generated before a `:left-join`. Without `:join-by`, a `:join` would normally be generated before a `:left-join`.

View file

@ -464,10 +464,16 @@
(defn- format-join-by (defn- format-join-by
"Clauses should be a sequence of join types followed "Clauses should be a sequence of join types followed
by their table and condition, so that you can construct by their table and condition, or a sequence of join
a series of joins in a specific order." clauses, so that you can construct a series of joins
in a specific order."
[_ clauses] [_ clauses]
(let [joins (partition-by ident? clauses)] (let [joins (if (every? map? clauses)
(into []
(comp (mapcat #(mapcat (juxt key val) %))
(map vector))
clauses)
(partition-by ident? clauses))]
(when-not (even? (count joins)) (when-not (even? (count joins))
(throw (ex-info ":join-by expects a sequence of join clauses" (throw (ex-info ":join-by expects a sequence of join clauses"
{:clauses clauses}))) {:clauses clauses})))

View file

@ -470,8 +470,8 @@
(-> (select :*) (-> (select :*)
(from :foo) (from :foo)
(join-by :left :bar [:= :foo.id :bar.id] (join-by :left [:bar [:= :foo.id :bar.id]]
:join :quux [:= :bar.qid = :quux.id]) :join [:quux [:= :bar.qid :quux.id]]))
This produces a LEFT JOIN followed by an INNER JOIN This produces a LEFT JOIN followed by an INNER JOIN
even though the 'natural' order for `left-join` and even though the 'natural' order for `left-join` and

View file

@ -232,6 +232,21 @@
:right-join [:bock [:= :bock.z :c.e]] :right-join [:bock [:= :bock.z :c.e]]
:left-join [[:clod :c] [:= :f.a :c.d]] :left-join [[:clod :c] [:= :f.a :c.d]]
:inner-join [:draq [:= :f.b :draq.x]]) :inner-join [:draq [:= :f.b :draq.x]])
(sql/format)))))
(testing "Specific JOIN orders with join clauses"
(is (= ["SELECT * FROM foo FULL JOIN beck ON beck.x = c.y RIGHT JOIN bock ON bock.z = c.e LEFT JOIN clod AS c ON f.a = c.d INNER JOIN draq ON f.b = draq.x"]
(sql/format {:select [:*] :from [:foo]
:join-by [{:full-join [:beck [:= :beck.x :c.y]]}
{:right-join [:bock [:= :bock.z :c.e]]}
{:left-join [[:clod :c] [:= :f.a :c.d]]}
{:join [:draq [:= :f.b :draq.x]]}]})))
(is (= ["SELECT * FROM foo FULL JOIN beck ON beck.x = c.y RIGHT JOIN bock ON bock.z = c.e LEFT JOIN clod AS c ON f.a = c.d INNER JOIN draq ON f.b = draq.x"]
(-> (select :*)
(from :foo)
(join-by (full-join :beck [:= :beck.x :c.y])
(right-join :bock [:= :bock.z :c.e])
(left-join [:clod :c] [:= :f.a :c.d])
(join :draq [:= :f.b :draq.x]))
(sql/format)))))) (sql/format))))))
(deftest test-cast (deftest test-cast