Fixes #235 by adding two different SET priority variants

This allows the behavior change in #200 to be worked around.
This commit is contained in:
Sean Corfield 2019-09-07 14:55:06 -07:00
parent 4ca74f2b0d
commit c573f3bd9c
5 changed files with 61 additions and 6 deletions

View file

@ -1,9 +1,10 @@
## Changes coming in 0.9.7
* Fix #248 by treating alias as "not a subquery" when generating SQL for it. (@seancorfield)
* Fix #139 by checking arguments to `columns`/`merge-columns` and throwing an exception if a single collection is supplied (instead of varargs).
* Fix #128 by adding `truncate` support.
* Fix #99 by adding a note to the first use of `select` in the README that column names can be keywords or symbols but not strings.
* Fix #235 by adding `set0` (`:set0`) and `set1` (`:set1`) variants of `sset` (`:set`) to support different placements of `SET` (before `FROM` or after `JOIN` respectively) that different databases require. See also #200. (@seancorfield)
* Fix #139 by checking arguments to `columns`/`merge-columns` and throwing an exception if a single collection is supplied (instead of varargs). (@seancorfield)
* Fix #128 by adding `truncate` support. (@seancorfield)
* Fix #99 by adding a note to the first use of `select` in the README that column names can be keywords or symbols but not strings. (@seancorfield)
## 0.9.6

View file

@ -203,6 +203,14 @@ with `clojure.core/set`):
"drama"]
```
If you are trying to build a compound update statement (with `from` or `join`),
be aware that different databases have slightly different syntax in terms of
where `SET` should appear. The default above is to put `SET` after any `JOIN`.
There are two variants of `sset` (and the underlying `:set` in the SQL map):
* `set0` (and `:set0`) -- this puts the `SET` before `FROM`,
* `set1` (and `:set1`) -- a synonym for `sset` (and `:set`) that puts the `SET` after `JOIN`.
Deletes look as you would expect:
```clojure

View file

@ -221,12 +221,14 @@
:delete-from 80
:truncate 85
:columns 90
:set0 100 ; low-priority set clause
:from 110
:join 120
:left-join 130
:right-join 140
:full-join 150
:set 155
:set1 156 ; high-priority set clause (synonym for :set)
:where 160
:group-by 170
:having 180
@ -624,6 +626,14 @@
(str "SET " (comma-join (for [[k v] values]
(str (to-sql k) " = " (to-sql v))))))
(defmethod format-clause :set0 [[_ values] _]
(str "SET " (comma-join (for [[k v] values]
(str (to-sql k) " = " (to-sql v))))))
(defmethod format-clause :set1 [[_ values] _]
(str "SET " (comma-join (for [[k v] values]
(str (to-sql k) " = " (to-sql v))))))
(defmethod format-clause :delete-from [[_ table] _]
(str "DELETE FROM " (to-sql table)))

View file

@ -283,9 +283,25 @@
;; short for sql set, to avoid name collision with clojure.core/set
(defn sset
([vs] (values nil vs))
([vs] (sset nil vs))
([m vs] (build-clause :set m vs)))
(defmethod build-clause :set0 [_ m values]
(assoc m :set0 values))
;; set with lower priority (before from)
(defn set0
([vs] (set0 nil vs))
([m vs] (build-clause :set0 m vs)))
(defmethod build-clause :set [_ m values]
(assoc m :set values))
;; set with higher priority (after join)
(defn set1
([vs] (set1 nil vs))
([m vs] (build-clause :set1 m vs)))
(defmethod build-clause :delete-from [_ m table]
(assoc m :delete-from table))

View file

@ -204,6 +204,18 @@
["WHERE (foo = ? AND bar = ?)" "foo" "bar"]))))
(deftest set-before-from ; issue 235
(is (=
["UPDATE `films` `f` SET `kind` = `c`.`test` FROM (SELECT `b`.`test` FROM `bar` `b` WHERE `b`.`id` = ?) `c` WHERE `f`.`kind` = ?" 1 "drama"]
(->
{:update [:films :f]
:set0 {:kind :c.test}
:from [[{:select [:b.test]
:from [[:bar :b]]
:where [:= :b.id 1]} :c]]
:where [:= :f.kind "drama"]}
(format :quoting :mysql)))))
(deftest set-after-join
(is (=
["UPDATE `foo` INNER JOIN `bar` ON `bar`.`id` = `foo`.`bar_id` SET `a` = ? WHERE `bar`.`b` = ?" 1 42]
@ -212,6 +224,14 @@
:join [:bar [:= :bar.id :foo.bar_id]]
:set {:a 1}
:where [:= :bar.b 42]}
(format :quoting :mysql))))
(is (=
["UPDATE `foo` INNER JOIN `bar` ON `bar`.`id` = `foo`.`bar_id` SET `a` = ? WHERE `bar`.`b` = ?" 1 42]
(->
{:update :foo
:join [:bar [:= :bar.id :foo.bar_id]]
:set1 {:a 1}
:where [:= :bar.b 42]}
(format :quoting :mysql)))))
(deftest delete-from-test