Restore upsert helper for #293

This commit is contained in:
Sean Corfield 2021-02-13 19:08:40 -08:00
parent 267eef778a
commit e585ded37e
5 changed files with 65 additions and 32 deletions

View file

@ -645,14 +645,6 @@ You can also register SQL clauses, specifying the keyword, the formatting functi
If you find yourself registering an operator, a function (syntax), or a new clause, consider submitting a [pull request to HoneySQL](https://github.com/seancorfield/honeysql/pulls) so others can use it, too. If it is dialect-specific, let me know in the pull request.
## TODO
- [ ] Create table, etc.
## Extensions
* [For PostgreSQL-specific extensions falling outside of ANSI SQL](https://github.com/nilenso/honeysql-postgres) -- these will all be core in 2.0!
## License
Copyright (c) 2020-2021 Sean Corfield. HoneySQL 1.x was copyright (c) 2012-2020 Justin Kramer and Sean Corfield.

View file

@ -646,6 +646,11 @@ reasonable choices.
(a keyword or symbol), or hash map of columns and
values, like `:set` (above), or a hash map of fields
(a sequence of SQL entities) and a where clause.
For convenience of building clauses with helpers,
it also accepts a sequence of one or more column
names followed by an optional hash map: this is treated
as an alternative form of the hash map with fields
and a where clause.
The single SQL entity and the list of fields produce
`SET` clauses using `EXCLUDED`:

View file

@ -495,22 +495,27 @@
{:clause x}))))
(defn- format-do-update-set [k x]
(if (map? x)
(if (and (or (contains? x :fields) (contains? x 'fields))
(or (contains? x :where) (contains? x 'where)))
(let [sets (str/join ", "
(map (fn [e]
(let [e (format-entity e {:drop-ns true})]
(str e " = EXCLUDED." e)))
(or (:fields x)
('fields x))))
[sql & params] (format-dsl {:where
(or (:where x)
('where x))})]
(into [(str (sql-kw k) " " sets " " sql)] params))
(format-set-exprs k x))
(let [e (format-entity x {:drop-ns true})]
[(str (sql-kw k) " " e " = EXCLUDED." e)])))
(cond (map? x)
(if (or (contains? x :fields) (contains? x 'fields))
(let [sets (str/join ", "
(map (fn [e]
(let [e (format-entity e {:drop-ns true})]
(str e " = EXCLUDED." e)))
(or (:fields x)
('fields x))))
where (or (:where x) ('where x))
[sql & params] (when where (format-dsl {:where where}))]
(into [(str (sql-kw k) " " sets
(when sql (str " " sql)))] params))
(format-set-exprs k x))
(sequential? x)
(let [[cols clauses] (split-with (complement map?) x)]
(if (seq cols)
(recur k {:fields cols :where (:where (first clauses))})
(recur k (first clauses))))
:else
(let [e (format-entity x {:drop-ns true})]
[(str (sql-kw k) " " e " = EXCLUDED." e)])))
(defn- format-simple-clause [c]
(binding [*inline* true]

View file

@ -106,7 +106,7 @@
(defn on-conflict [& args] (generic-1 :on-conflict args))
(defn on-constraint [& args] (generic :on-constraint args))
(defn do-nothing [& args] (generic :do-nothing args))
(defn do-update-set [& args] (generic-1 :do-update-set args))
(defn do-update-set [& args] (generic :do-update-set args))
(defn returning [& args] (generic :returning args))
;; helpers that produce non-clause expressions -- must be listed below:
@ -114,8 +114,21 @@
;; to make this easy to use in a select, wrap it so it becomes a function:
(defn over [& args] [(into [:over] args)])
;; helper to ease compatibility with former nilenso/honeysql-postgres code:
(defn upsert [data & clauses] (default-merge data clauses))
;; this helper is intended to ease the migration from nilenso:
(defn upsert
([clause] (upsert {} clause))
([data clause]
(let [{:keys [on-conflict do-nothing do-update-set where]} clause]
(cond-> data
on-conflict
(assoc :on-conflict on-conflict)
do-nothing
(assoc :do-nothing do-nothing)
do-update-set
(assoc :do-update-set (if where
{:fields do-update-set
:where where}
do-update-set))))))
#?(:clj
(assert (= (clojure.core/set (conj @@#'h/base-clause-order

View file

@ -14,9 +14,7 @@
;; pull in all the PostgreSQL helpers that the nilenso
;; library provided (as well as the regular HoneySQL ones):
[honey.sql.helpers :as sqlh :refer
[;; not needed because on-conflict accepts clauses
#_upsert
on-conflict do-nothing on-constraint
[upsert on-conflict do-nothing on-constraint
returning do-update-set
;; not needed because do-update-set can do this directly
#_do-update-set!
@ -44,18 +42,38 @@
(do-update-set :dname)
(returning :*)
sql/format)))
(is (= ["INSERT INTO distributors (did, dname) VALUES (?, ?), (?, ?) ON CONFLICT (did) DO UPDATE SET dname = EXCLUDED.dname RETURNING *" 5 "Gizmo Transglobal" 6 "Associated Computing, Inc"]
(-> (insert-into :distributors)
(values [{:did 5 :dname "Gizmo Transglobal"}
{:did 6 :dname "Associated Computing, Inc"}])
(upsert (-> (on-conflict :did)
(do-update-set :dname)))
(returning :*)
sql/format)))
(is (= ["INSERT INTO distributors (did, dname) VALUES (?, ?) ON CONFLICT (did) DO NOTHING" 7 "Redline GmbH"]
(-> (insert-into :distributors)
(values [{:did 7 :dname "Redline GmbH"}])
(on-conflict :did)
do-nothing
sql/format)))
(is (= ["INSERT INTO distributors (did, dname) VALUES (?, ?) ON CONFLICT (did) DO NOTHING" 7 "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 (?, ?) ON CONFLICT ON CONSTRAINT distributors_pkey DO NOTHING" 9 "Antwerp Design"]
(-> (insert-into :distributors)
(values [{:did 9 :dname "Antwerp Design"}])
(on-conflict (on-constraint :distributors_pkey))
do-nothing
sql/format)))
(is (= ["INSERT INTO distributors (did, dname) VALUES (?, ?) ON CONFLICT ON CONSTRAINT distributors_pkey DO NOTHING" 9 "Antwerp Design"]
(-> (insert-into :distributors)
(values [{:did 9 :dname "Antwerp Design"}])
(upsert (-> (on-conflict (on-constraint :distributors_pkey))
do-nothing))
sql/format)))
(is (= ["INSERT INTO distributors (did, dname) VALUES (?, ?), (?, ?) ON CONFLICT (did) DO UPDATE SET dname = EXCLUDED.dname" 10 "Pinp Design" 11 "Foo Bar Works"]
(sql/format {:insert-into :distributors
:values [{:did 10 :dname "Pinp Design"}
@ -73,8 +91,8 @@
(-> (insert-into [:distributors [:did :dname]]
(select 1 "whatever"))
#_(query-values (select 1 "whatever"))
(on-conflict (on-constraint :distributors_pkey))
do-nothing
(upsert (-> (on-conflict (on-constraint :distributors_pkey))
do-nothing))
sql/format)))))
(deftest upsert-where-test