Fixes #308 by adding support for clauses
This also corrects the docstring for the join-by helper.
This commit is contained in:
parent
d734767877
commit
e6a5bdb001
5 changed files with 48 additions and 9 deletions
|
|
@ -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).
|
||||||
|
|
|
||||||
|
|
@ -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`.
|
||||||
|
|
|
||||||
|
|
@ -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})))
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue