fix #494 by supporting expressions in on conflict

This commit is contained in:
Sean Corfield 2023-06-20 12:11:00 -07:00
parent 3ec884f881
commit 290537c581
4 changed files with 23 additions and 18 deletions

View file

@ -1,6 +1,7 @@
# Changes # Changes
* 2.4.next in progress * 2.4.next in progress
* Fix [#494](https://github.com/seancorfield/honeysql/issues/494) by supporting expressions in `:on-conflict` instead of just entities.
* Address [#493](https://github.com/seancorfield/honeysql/issues/493) by clarifying use of `:values` in CTEs (using `:with`). * Address [#493](https://github.com/seancorfield/honeysql/issues/493) by clarifying use of `:values` in CTEs (using `:with`).
* Address [#489](https://github.com/seancorfield/honeysql/issues/489) by adding more examples around `:update`. * Address [#489](https://github.com/seancorfield/honeysql/issues/489) by adding more examples around `:update`.
* Update dev/test dependencies. * Update dev/test dependencies.

View file

@ -1099,10 +1099,11 @@ as if they are separate clauses but they will appear
in pairs: `ON ... DO ...`. in pairs: `ON ... DO ...`.
`:on-conflict` accepts a sequence of zero or more `:on-conflict` accepts a sequence of zero or more
SQL entities (keywords or symbols), optionally SQL expressions, optionally
followed by a single SQL clause (hash map). It can also followed by a single SQL clause (hash map). It can also
accept either a single SQL entity or a single SQL clause. accept either a single SQL entity or a single SQL clause.
The SQL entities are column names and the SQL clause can be an The SQL expressions can be just column names or function calls etc,
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]_

View file

@ -881,23 +881,26 @@
(defn- format-on-conflict [k x] (defn- format-on-conflict [k x]
(if (sequential? x) (if (sequential? x)
(let [entities (take-while ident? x) (let [exprs (take-while (complement map?) x)
n (count entities) n (count exprs)
[clause & more] (drop n x) [clause & more] (drop n x)
_ (when (or (seq more) _ (when (seq more)
(and clause (not (map? clause))))
(throw (ex-info "unsupported :on-conflict format" (throw (ex-info "unsupported :on-conflict format"
{:clause x}))) {:clause x})))
[sql & params] (when clause [sqls expr-params]
(when (seq exprs)
(format-expr-list exprs))
[sql & clause-params]
(when clause
(format-dsl clause))] (format-dsl clause))]
(into [(str (sql-kw k)
(-> [(str (sql-kw k)
(when (pos? n) (when (pos? n)
(str " (" (str " (" (str/join ", " sqls) ")"))
(str/join ", " (map #'format-entity entities))
")"))
(when sql (when sql
(str " " sql)))] (str " " sql)))]
params)) (into expr-params)
(into clause-params)))
(format-on-conflict k [x]))) (format-on-conflict k [x])))
(defn- format-do-update-set [k x] (defn- format-do-update-set [k x]

View file

@ -772,13 +772,13 @@ DO NOTHING
INSERT INTO customers INSERT INTO customers
(name, email) (name, email)
VALUES ('Microsoft', 'hotline@microsoft.com') VALUES ('Microsoft', 'hotline@microsoft.com')
ON CONFLICT (name, email) ON CONFLICT (name, TRIM(email))
DO NOTHING DO NOTHING
"] "]
(format {:insert-into :customers (format {:insert-into :customers
:columns [:name :email] :columns [:name :email]
:values [[[:inline "Microsoft"], [:inline "hotline@microsoft.com"]]] :values [[[:inline "Microsoft"], [:inline "hotline@microsoft.com"]]]
:on-conflict [:name :email] :on-conflict [:name [:trim :email]]
:do-nothing true} :do-nothing true}
{:pretty true}))) {:pretty true})))
(is (= [" (is (= ["