Add postgres extension - upsert & returning
This commit is contained in:
parent
43df955a86
commit
c53ec6c78d
3 changed files with 136 additions and 0 deletions
61
src/honeysql/postgres/format.clj
Normal file
61
src/honeysql/postgres/format.clj
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
(ns honeysql.postgres.format
|
||||
(:refer-clojure :exclude [format])
|
||||
(:require [honeysql.format :refer :all]))
|
||||
|
||||
;; Move the whole default priorities here ?
|
||||
(def postgres-clause-priorities
|
||||
{:upsert 225
|
||||
:on-conflict 230
|
||||
:on-conflict-constraint 230
|
||||
:do-update-set 235
|
||||
:do-update-set! 235
|
||||
:do-nothing 235
|
||||
:returning 240
|
||||
:query-values 250})
|
||||
|
||||
;; FIXME : Not sure if this is the best way to implement this, but since the `clause-store` is being used
|
||||
;; by format to decide the order of clauses, not really sure what would be a better implementation.
|
||||
(doseq [[k v] postgres-clause-priorities]
|
||||
(register-clause! k v))
|
||||
|
||||
(defn get-first [x]
|
||||
(if (coll? x)
|
||||
(first x)
|
||||
x))
|
||||
|
||||
(defmethod format-clause :on-conflict-constraint [[_ k] _]
|
||||
(str "ON CONFLICT ON CONSTRAINT " (-> k
|
||||
get-first
|
||||
to-sql)))
|
||||
|
||||
(defmethod format-clause :on-conflict [[_ id] _]
|
||||
(str "ON CONFLICT (" (-> id
|
||||
get-first
|
||||
to-sql) ")"))
|
||||
|
||||
(defmethod format-clause :do-nothing [_ _]
|
||||
"DO NOTHING")
|
||||
|
||||
;; Used when there is a need to update the columns with modified values if the
|
||||
;; row(id) already exits - accepts a map of column and value
|
||||
(defmethod format-clause :do-update-set! [[_ values] _]
|
||||
(str "DO UPDATE SET " (comma-join (for [[k v] values]
|
||||
(str (to-sql k) " = " (to-sql v))))))
|
||||
|
||||
;; Used when it is a simple upsert - accepts a vector of columns
|
||||
(defmethod format-clause :do-update-set [[_ values] _]
|
||||
(str "DO UPDATE SET "
|
||||
(comma-join (map #(str (to-sql %) " = EXCLUDED." (to-sql %))
|
||||
values))))
|
||||
|
||||
(defn format-upsert-clause [upsert]
|
||||
(let [ks (keys upsert)]
|
||||
(map #(format-clause % (find upsert %)) upsert)))
|
||||
|
||||
;; Accepts a map with the following possible keys
|
||||
;; :on-conflict, :do-update-set or :do-update-set! or :do-nothing
|
||||
(defmethod format-clause :upsert [[_ upsert] _]
|
||||
(space-join (format-upsert-clause upsert)))
|
||||
|
||||
(defmethod format-clause :returning [[_ fields] _]
|
||||
(str "RETURNING " (comma-join (map to-sql fields))))
|
||||
26
src/honeysql/postgres/helpers.clj
Normal file
26
src/honeysql/postgres/helpers.clj
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
(ns honeysql.postgres.helpers
|
||||
(:refer-clojure :exclude [update])
|
||||
(:require [honeysql.helpers :refer :all]))
|
||||
|
||||
(defn do-nothing [m]
|
||||
(assoc m :do-nothing []))
|
||||
|
||||
(defhelper do-update-set [m args]
|
||||
(assoc m :do-update-set (collify args)))
|
||||
|
||||
(defhelper db-update-set! [m args]
|
||||
(assoc m :do-update-set! args))
|
||||
|
||||
(defhelper on-conflict [m args]
|
||||
(assoc m :on-conflict args))
|
||||
|
||||
(defhelper on-conflict-constraint [m args]
|
||||
(assoc m :on-conflict-constraint args))
|
||||
|
||||
(defhelper upsert [m args]
|
||||
(if (plain-map? args)
|
||||
(assoc m :upsert args)
|
||||
(assoc m :upsert (first args))))
|
||||
|
||||
(defhelper returning [m fields]
|
||||
(assoc m :returning (collify fields)))
|
||||
49
test/honeysql/postgres_test.clj
Normal file
49
test/honeysql/postgres_test.clj
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
(ns honeysql.postgres-test
|
||||
(:refer-clojure :exclude [update])
|
||||
(:require [honeysql.postgres.format :refer :all]
|
||||
[honeysql.postgres.helpers :refer :all]
|
||||
[honeysql.helpers :refer :all]
|
||||
[honeysql.format :as sql]
|
||||
[clojure.test :refer :all]))
|
||||
|
||||
(deftest upsert-test
|
||||
(testing "upsert sql generation for postgresql"
|
||||
(is (= ["INSERT INTO distributors d (did, dname) VALUES (5, ?), (6, ?) ON CONFLICT (did) DO UPDATE SET dname = EXCLUDED.dname RETURNING d.*" "Gizmo Transglobal" "Associated Computing, Inc"]
|
||||
(-> (insert-into [:distributors :d])
|
||||
(values [{:did 5 :dname "Gizmo Transglobal"}
|
||||
{:did 6 :dname "Associated Computing, Inc"}])
|
||||
(upsert (-> (on-conflict :did)
|
||||
(do-update-set :dname)))
|
||||
(returning :d.*)
|
||||
sql/format)))
|
||||
(is (= ["INSERT INTO distributors (did, dname) VALUES (7, ?) ON CONFLICT (did) DO NOTHING" "Redline GmbH"]
|
||||
(-> (insert-into :distributors)
|
||||
(values [{:did 7 :dname "Redline GmbH"}])
|
||||
(upsert (-> (on-conflict :did)
|
||||
do-nothing))
|
||||
sql/format)))
|
||||
(is (= ["INSERT INTO distributors (did, dname) VALUES (9, ?) ON CONFLICT ON CONSTRAINT distributors_pkey DO NOTHING" "Antwerp Design"]
|
||||
(-> (insert-into :distributors)
|
||||
(values [{:did 9 :dname "Antwerp Design"}])
|
||||
(upsert (-> (on-conflict-constraint :distributors_pkey)
|
||||
do-nothing))
|
||||
sql/format)))
|
||||
(is (= ["INSERT INTO distributors (did, dname) VALUES (10, ?), (11, ?) ON CONFLICT (did) DO UPDATE SET dname = EXCLUDED.dname" "Pinp Design" "Foo Bar Works"]
|
||||
(sql/format {:insert-into :distributors
|
||||
:values [{:did 10 :dname "Pinp Design"}
|
||||
{:did 11 :dname "Foo Bar Works"}]
|
||||
:upsert {:on-conflict :did
|
||||
:do-update-set [:dname]}})))))
|
||||
|
||||
(deftest returning-test
|
||||
(testing "returning clause in sql generation for postgresql"
|
||||
(is (= ["DELETE FROM distributors WHERE did > 10 RETURNING *"]
|
||||
(sql/format {:delete-from :distributors
|
||||
:where [:> :did :10]
|
||||
:returning [:*] })))
|
||||
(is (= ["UPDATE distributors SET dname = ? WHERE did = 2 RETURNING did dname" "Foo Bar Designs"]
|
||||
(-> (update :distributors)
|
||||
(sset {:dname "Foo Bar Designs"})
|
||||
(where [:= :did :2])
|
||||
(returning [:did :dname])
|
||||
sql/format)))))
|
||||
Loading…
Reference in a new issue