diff --git a/CHANGELOG.md b/CHANGELOG.md index 24c7028..8b389f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changes +* 2.4.next in progress + * Address [#481](https://github.com/seancorfield/honeysql/issues/481) by adding more examples around `:do-update-set`. + * 2.4.1006 -- 2023-03-17 * Fix [#478](https://github.com/seancorfield/honeysql/issues/478) by handling `:do-update-set` correctly in the `upsert` helper and by handling parameters correctly in the `:do-update-set` formatter. * Fix [#476](https://github.com/seancorfield/honeysql/issues/476) by adding support for multiple arguments to `:raw`, essentially restoring 1.x functionality (while still allowing for embedded vectors as expressions, introduced in 2.x). diff --git a/doc/clause-reference.md b/doc/clause-reference.md index e548c96..aea4a18 100644 --- a/doc/clause-reference.md +++ b/doc/clause-reference.md @@ -1124,6 +1124,12 @@ user=> (sql/format {:insert-into :companies :do-update-set {:fields [:name] :where [:<> :name nil]}}) ["INSERT INTO companies (name) VALUES (?) ON CONFLICT (name) DO UPDATE SET name = EXCLUDED.name WHERE name IS NOT NULL" "Microsoft"] +user=> (sql/format {:insert-into :companies + :values [{:name "Microsoft"}] + :on-conflict :name + :do-update-set {:fields {:name [:+ :table.name 1]} + :where [:<> :name nil]}}) +["INSERT INTO companies (name) VALUES (?) ON CONFLICT (name) DO UPDATE SET name = table.name + ? WHERE name IS NOT NULL" "Microsoft" 1] user=> (sql/format {:insert-into :companies :values [{:name "Microsoft"}] :on-conflict {:on-constraint :name-idx} diff --git a/doc/postgresql.md b/doc/postgresql.md index 5baf235..dfcca9b 100644 --- a/doc/postgresql.md +++ b/doc/postgresql.md @@ -255,6 +255,71 @@ By comparison, this is the DSL structure that nilenso would have required: :where [:= :user.active false]}}} ``` +All of the examples for `:do-update-set` so far provide one or +more columns and generated `SET` clauses using `EXCLUDED` columns. +You can also perform regular `SET` operations, where the right-hand +side is a full SQL expression by specifying a hash map of column / +expression pairs, like you would for a regular `:set` clause: + +```clojure +user=> (-> (insert-into :table) + (values [{:id "id" :counter 1}]) + (on-conflict :id) + (do-update-set {:counter [:+ :table.counter 1]}) + (sql/format {:pretty true})) +[" +INSERT INTO table +(id, counter) VALUES (?, ?) +ON CONFLICT (id) +DO UPDATE SET counter = table.counter + ? +" "id" 1 1] +;; using the DSL directly: +user=> (-> {:insert-into :table + :values [{:id "id" :counter 1}] + :on-conflict :id + :do-update-set {:counter [:+ :table.counter 1]}} + (sql/format {:pretty true})) +[" +INSERT INTO table +(id, counter) VALUES (?, ?) +ON CONFLICT (id) +DO UPDATE SET counter = table.counter + ? +" "id" 1 1] +``` + +If you need to combine a `DO UPDATE SET` hash map expression +with a `WHERE` clause, you need to explicitly use the `:fields` / +`:where` format explained above. Here's how those two examples +look with a `WHERE` clause added: + +```clojure +user=> (-> (insert-into :table) + (values [{:id "id" :counter 1}]) + (on-conflict :id) + (do-update-set {:fields {:counter [:+ :table.counter 1]} + :where [:> :table.counter 1]}) + (sql/format {:pretty true})) +[" +INSERT INTO table +(id, counter) VALUES (?, ?) +ON CONFLICT (id) +DO UPDATE SET counter = table.counter + ? WHERE table.counter > ? +" "id" 1 1 1] +;; using the DSL directly: +user=> (-> {:insert-into :table + :values [{:id "id" :counter 1}] + :on-conflict :id + :do-update-set {:fields {:counter [:+ :table.counter 1]} + :where [:> :table.counter 1]}} + (sql/format {:pretty true})) +[" +INSERT INTO table +(id, counter) VALUES (?, ?) +ON CONFLICT (id) +DO UPDATE SET counter = table.counter + ? WHERE table.counter > ? +" "id" 1 1 1] +``` + ## INSERT INTO AS HoneySQL supports aliases directly in `:insert-into` so no special