Fixes #323 by allowing multiple column names
This commit is contained in:
parent
e227e1b9ab
commit
f606dc6044
6 changed files with 70 additions and 28 deletions
|
|
@ -1,5 +1,8 @@
|
||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
|
* 2.0.next in progress
|
||||||
|
* Fix #323 by supporting more than one SQL entity in `:on-conflict`.
|
||||||
|
|
||||||
* 2.0.0-beta2 (for testing; 2021-04-13)
|
* 2.0.0-beta2 (for testing; 2021-04-13)
|
||||||
* The documentation continues to be expanded and clarified in response to feedback!
|
* The documentation continues to be expanded and clarified in response to feedback!
|
||||||
* Fix #322 by rewriting/simplifying `WHERE`/`HAVING` merge logic. **Important bug fix!**
|
* Fix #322 by rewriting/simplifying `WHERE`/`HAVING` merge logic. **Important bug fix!**
|
||||||
|
|
|
||||||
|
|
@ -815,10 +815,11 @@ These are grouped together because they are handled
|
||||||
as if they are separate clauses but they will appear
|
as if they are separate clauses but they will appear
|
||||||
in pairs: `ON ... DO ...`.
|
in pairs: `ON ... DO ...`.
|
||||||
|
|
||||||
`:on-conflict` accepts either a single SQL entity
|
`:on-conflict` accepts a sequence of zero or more
|
||||||
(a keyword or symbol), or a SQL clause, or a pair
|
SQL entities (keywords or symbols), optionally
|
||||||
of a SQL entity and a SQL clause. The SQL entity is
|
followed by a single SQL clause (hash map). It can also
|
||||||
a column name and the SQL clause can be an
|
accept either a single SQL entity or a single SQL clause.
|
||||||
|
The SQL entities are column names and the SQL clause can be an
|
||||||
`:on-constraint` clause or a`:where` clause.
|
`:on-constraint` clause or a`:where` clause.
|
||||||
|
|
||||||
_[For convenience of use with the `on-conflict` helper, this clause can also accept any of those arguments, wrapped in a sequence; it can also accept an empty sequence, and just produce `ON CONFLICT`, so that it can be combined with other clauses directly]_
|
_[For convenience of use with the `on-conflict` helper, this clause can also accept any of those arguments, wrapped in a sequence; it can also accept an empty sequence, and just produce `ON CONFLICT`, so that it can be combined with other clauses directly]_
|
||||||
|
|
|
||||||
|
|
@ -589,26 +589,25 @@
|
||||||
(into [(str (sql-kw k) " " (str/join ", " sqls))] params)))
|
(into [(str (sql-kw k) " " (str/join ", " sqls))] params)))
|
||||||
|
|
||||||
(defn- format-on-conflict [k x]
|
(defn- format-on-conflict [k x]
|
||||||
(cond (ident? x)
|
(if (sequential? x)
|
||||||
[(str (sql-kw k) " (" (format-entity x) ")")]
|
(let [entities (take-while ident? x)
|
||||||
(map? x)
|
n (count entities)
|
||||||
(let [[sql & params] (format-dsl x)]
|
[clause & more] (drop n x)
|
||||||
(into [(str (sql-kw k) " " sql)] params))
|
_ (when (or (seq more)
|
||||||
(and (sequential? x)
|
(and clause (not (map? clause))))
|
||||||
(ident? (first x))
|
(throw (ex-info "unsupported :on-conflict format"
|
||||||
(map? (second x)))
|
{:clause x})))
|
||||||
(let [[sql & params] (format-dsl (second x))]
|
[sql & params] (when clause
|
||||||
(into [(str (sql-kw k)
|
(format-dsl clause))]
|
||||||
" (" (format-entity (first x)) ") "
|
(into [(str (sql-kw k)
|
||||||
sql)]
|
(when (pos? n)
|
||||||
params))
|
(str " ("
|
||||||
(and (sequential? x) (= 1 (count x)))
|
(str/join ", " (map #'format-entity entities))
|
||||||
(format-on-conflict k (first x))
|
")"))
|
||||||
(and (sequential? x) (= 0 (count x)))
|
(when sql
|
||||||
[(sql-kw k)]
|
(str " " sql)))]
|
||||||
:else
|
params))
|
||||||
(throw (ex-info "unsupported :on-conflict format"
|
(format-on-conflict k [x])))
|
||||||
{:clause x}))))
|
|
||||||
|
|
||||||
(defn- format-do-update-set [k x]
|
(defn- format-do-update-set [k x]
|
||||||
(cond (map? x)
|
(cond (map? x)
|
||||||
|
|
|
||||||
|
|
@ -768,10 +768,9 @@
|
||||||
(generic-1 :values args))
|
(generic-1 :values args))
|
||||||
|
|
||||||
(defn on-conflict
|
(defn on-conflict
|
||||||
"Accepts a single column name to detect conflicts
|
"Accepts zero or more SQL entities (keywords or symbols),
|
||||||
during an upsert, optionally followed by a `WHERE`
|
optionally followed by a single SQL clause (hash map)."
|
||||||
clause."
|
{:arglists '([column* where-clause])}
|
||||||
{:arglists '([column] [column where-clause])}
|
|
||||||
[& args]
|
[& args]
|
||||||
(generic :on-conflict args))
|
(generic :on-conflict args))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,20 @@
|
||||||
(on-conflict (on-constraint :distributors_pkey))
|
(on-conflict (on-constraint :distributors_pkey))
|
||||||
do-nothing
|
do-nothing
|
||||||
sql/format)))
|
sql/format)))
|
||||||
|
(is (= ["INSERT INTO distributors (did, dname) VALUES (?, ?) ON CONFLICT (did) ON CONSTRAINT distributors_pkey DO NOTHING" 9 "Antwerp Design"]
|
||||||
|
;; with both name and clause:
|
||||||
|
(-> (insert-into :distributors)
|
||||||
|
(values [{:did 9 :dname "Antwerp Design"}])
|
||||||
|
(on-conflict :did (on-constraint :distributors_pkey))
|
||||||
|
do-nothing
|
||||||
|
sql/format)))
|
||||||
|
(is (= ["INSERT INTO distributors (did, dname) VALUES (?, ?) ON CONFLICT (did, dname) ON CONSTRAINT distributors_pkey DO NOTHING" 9 "Antwerp Design"]
|
||||||
|
;; with multiple names and a clause:
|
||||||
|
(-> (insert-into :distributors)
|
||||||
|
(values [{:did 9 :dname "Antwerp Design"}])
|
||||||
|
(on-conflict :did :dname (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"]
|
(is (= ["INSERT INTO distributors (did, dname) VALUES (?, ?) ON CONFLICT ON CONSTRAINT distributors_pkey DO NOTHING" 9 "Antwerp Design"]
|
||||||
;; almost identical to nilenso version:
|
;; almost identical to nilenso version:
|
||||||
(-> (insert-into :distributors)
|
(-> (insert-into :distributors)
|
||||||
|
|
|
||||||
|
|
@ -618,6 +618,32 @@ INSERT INTO customers
|
||||||
(name, email)
|
(name, email)
|
||||||
VALUES ('Microsoft', 'hotline@microsoft.com')
|
VALUES ('Microsoft', 'hotline@microsoft.com')
|
||||||
ON CONFLICT (name)
|
ON CONFLICT (name)
|
||||||
|
DO NOTHING
|
||||||
|
"]
|
||||||
|
(format {:insert-into :customers
|
||||||
|
:columns [:name :email]
|
||||||
|
:values [[[:inline "Microsoft"], [:inline "hotline@microsoft.com"]]]
|
||||||
|
:on-conflict [:name]
|
||||||
|
:do-nothing true}
|
||||||
|
{:pretty true})))
|
||||||
|
(is (= ["
|
||||||
|
INSERT INTO customers
|
||||||
|
(name, email)
|
||||||
|
VALUES ('Microsoft', 'hotline@microsoft.com')
|
||||||
|
ON CONFLICT (name, email)
|
||||||
|
DO NOTHING
|
||||||
|
"]
|
||||||
|
(format {:insert-into :customers
|
||||||
|
:columns [:name :email]
|
||||||
|
:values [[[:inline "Microsoft"], [:inline "hotline@microsoft.com"]]]
|
||||||
|
:on-conflict [:name :email]
|
||||||
|
:do-nothing true}
|
||||||
|
{:pretty true})))
|
||||||
|
(is (= ["
|
||||||
|
INSERT INTO customers
|
||||||
|
(name, email)
|
||||||
|
VALUES ('Microsoft', 'hotline@microsoft.com')
|
||||||
|
ON CONFLICT (name)
|
||||||
DO UPDATE SET email = EXCLUDED.email || ';' || customers.email
|
DO UPDATE SET email = EXCLUDED.email || ';' || customers.email
|
||||||
"]
|
"]
|
||||||
(format {:insert-into :customers
|
(format {:insert-into :customers
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue