Allow mysql upserts

While I have a bespoke version of this externally, it would be nice to
gracefully take advantage of honeysql's reader literals for sql fn calls
and the like.
This commit is contained in:
Donald Ball 2015-08-22 21:21:40 -04:00
parent 7001826d4b
commit 910d1c72a1
5 changed files with 55 additions and 1 deletions

View file

@ -1,5 +1,7 @@
## 0.6.2 In development ## 0.6.2 In development
* Support mysql upserts (@dball)
## 0.6.1 ## 0.6.1
* Define parameterizable protocol on nil (@dball) * Define parameterizable protocol on nil (@dball)

View file

@ -143,6 +143,22 @@ The column values do not have to be literals, they can be nested queries:
"user"] "user"]
``` ```
MySQL upserts are supported:
```clj
(-> (insert-into :properties)
(columns :name :surname :age)
(values
[["Jon" "Smith" 34]
["Andrew" "Cooper" 12]
["Jane" "Daniels" 56]])
(upsert :mysql {:age #sql/call [:values :age]})
sql/format)
=> ["INSERT INTO properties (name, surname, age)
VALUES (?, ?, 34), (?, ?, 12), (?, ?, 56)"
"Jon" "Smith" "Andrew" "Cooper" "Jane" "Daniels"]
```
Updates are possible too (note the double S in `sset` to avoid clashing Updates are possible too (note the double S in `sset` to avoid clashing
with `clojure.core/set`): with `clojure.core/set`):

View file

@ -191,6 +191,7 @@
:offset 210 :offset 210
:lock 215 :lock 215
:values 220 :values 220
:upsert 225
:query-values 230}) :query-values 230})
(def clause-store (atom default-clause-priorities)) (def clause-store (atom default-clause-priorities))
@ -495,6 +496,22 @@
(comma-join (for [x values] (comma-join (for [x values]
(str "(" (comma-join (map to-sql (vals x))) ")")))))) (str "(" (comma-join (map to-sql (vals x))) ")"))))))
(defmulti format-upsert-clause
(fn [mode updates] mode))
(defmethod format-upsert-clause :mysql
[_ updates]
(when-not (seq updates)
(let [msg "upserts clause must have an :updates map"]
(throw (IllegalArgumentException. msg))))
(str "ON DUPLICATE KEY UPDATE "
(comma-join (for [[column value] updates]
(str (to-sql column) "=" (to-sql value))))))
(defmethod format-clause :upsert [[_ upsert] _]
(let [{:keys [mode updates]} upsert]
(format-upsert-clause mode updates)))
(defmethod format-clause :query-values [[_ query-values] _] (defmethod format-clause :query-values [[_ query-values] _]
(to-sql query-values)) (to-sql query-values))

View file

@ -159,6 +159,9 @@
lock lock
(assoc :lock lock))) (assoc :lock lock)))
(defhelper upsert [m mode updates]
(assoc m :upsert {:mode mode :updates updates}))
(defhelper modifiers [m ms] (defhelper modifiers [m ms]
(if (nil? ms) (if (nil? ms)
m m

View file

@ -32,7 +32,23 @@
(is (= (format-clause (first {:insert-into [:foo {:select [:bar] :from [:baz]}]}) nil) (is (= (format-clause (first {:insert-into [:foo {:select [:bar] :from [:baz]}]}) nil)
"INSERT INTO foo SELECT bar FROM baz")) "INSERT INTO foo SELECT bar FROM baz"))
(is (= (format-clause (first {:insert-into [[:foo [:a :b :c]] {:select [:d :e :f] :from [:baz]}]}) nil) (is (= (format-clause (first {:insert-into [[:foo [:a :b :c]] {:select [:d :e :f] :from [:baz]}]}) nil)
"INSERT INTO foo (a, b, c) SELECT d, e, f FROM baz"))) "INSERT INTO foo (a, b, c) SELECT d, e, f FROM baz"))
(is (= (format {:insert-into :letters
:columns [:domain_key]
:values [["a"] ["b"] ["c"]]})
["INSERT INTO letters (domain_key) VALUES (?), (?), (?)" "a" "b" "c"]))
(is (= (format {:insert-into :letters
:columns [:domain_key]
:values [["a"] ["b"] ["c"]]
:upsert {:mode :mysql
:updates {:id :id}}})
["INSERT INTO letters (domain_key) VALUES (?), (?), (?) ON DUPLICATE KEY UPDATE id=id" "a" "b" "c"]))
(is (= (format {:insert-into :letters
:columns [:domain_key :rank]
:values [["a" 1] ["b" 2] ["c" 3]]
:upsert {:mode :mysql
:updates {:rank #sql/call [:values :rank]}}})
["INSERT INTO letters (domain_key, rank) VALUES (?, 1), (?, 2), (?, 3) ON DUPLICATE KEY UPDATE rank=values(rank)" "a" "b" "c"])))
(deftest exists-test (deftest exists-test
(is (= (format {:exists {:select [:a] :from [:foo]}}) (is (= (format {:exists {:select [:a] :from [:foo]}})