diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b1ffbd..41101fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * Support much richer range of syntax on `CREATE`/`DROP` statements in general, including columns, `TABLESPACE`, `CASCADE`, `WITH [NO] DATA`, etc. * Fix #306 by supporting `CREATE TABLE .. AS ..`. * Fix #305 by supporting more complex join clauses. + * Fix #303 by supporting MySQL's `ON DUPLICATE KEY UPDATE`. * Fix #301 by adding support for `CREATE`/`DROP`/`REFRESH` on `MATERIALIZED VIEW`. * Add tests to confirm #299 does not affect v2. * Confirm the whole of the [nilenso/honeysql-postgres](https://github.com/nilenso/honeysql-postgres) is implemented out-of-the-box (#293). diff --git a/doc/clause-reference.md b/doc/clause-reference.md index 4cb1a7a..0459e21 100644 --- a/doc/clause-reference.md +++ b/doc/clause-reference.md @@ -715,6 +715,10 @@ user=> (sql/format {:insert-into :companies ["INSERT INTO companies (name) VALUES (?) ON CONFLICT ON CONSTRAINT name_idx DO NOTHING" "Microsoft"] ``` +## on-duplicate-key-update + +This is the MySQL equivalent of `on-update-set` described above. + ## returning `:returning` accepts a single sequence argument that lists diff --git a/src/honey/sql.cljc b/src/honey/sql.cljc index 5eeb92d..b7f29d7 100644 --- a/src/honey/sql.cljc +++ b/src/honey/sql.cljc @@ -54,7 +54,7 @@ :where :group-by :having :window :partition-by :order-by :limit :offset :for :values - :on-conflict :on-constraint :do-nothing :do-update-set + :on-conflict :on-constraint :do-nothing :do-update-set :on-duplicate-key-update :returning :with-data]) @@ -754,6 +754,8 @@ :on-constraint #'format-selector :do-nothing (fn [k _] (vector (sql-kw k))) :do-update-set #'format-do-update-set + ;; MySQL-specific but might as well be always enabled: + :on-duplicate-key-update #'format-do-update-set :returning #'format-selects :with-data #'format-with-data})) diff --git a/src/honey/sql/helpers.cljc b/src/honey/sql/helpers.cljc index 222ab61..844c619 100644 --- a/src/honey/sql/helpers.cljc +++ b/src/honey/sql/helpers.cljc @@ -569,6 +569,10 @@ [& args] (generic :do-update-set args)) +(defn on-duplicate-key-update + [& args] + (generic :on-duplicate-key-update args)) + (defn returning "Accepts any number of column names to return from an insert operation: diff --git a/test/honey/sql/helpers_test.cljc b/test/honey/sql/helpers_test.cljc index 686e8cf..c894d08 100644 --- a/test/honey/sql/helpers_test.cljc +++ b/test/honey/sql/helpers_test.cljc @@ -10,8 +10,9 @@ create-materialized-view drop-view drop-materialized-view cross-join do-update-set drop-column drop-index drop-table from full-join group-by having insert-into - join-by join left-join limit offset on-conflict order-by - over partition-by refresh-materialized-view + join-by join left-join limit offset on-conflict + on-duplicate-key-update + order-by over partition-by refresh-materialized-view rename-column rename-table returning right-join select select-distinct values where window with with-columns with-data]])) @@ -649,3 +650,24 @@ (f {k [:and [:a] [:b]]} :or [:x] [:y])))))))) + +(deftest mysql-on-duplicate-key-update + (testing "From https://www.mysqltutorial.org/mysql-insert-or-update-on-duplicate-key-update" + (is (= (sql/format (-> (insert-into :device) + (columns :name) + (values [["Printer"]]) + (on-duplicate-key-update {:name "Printer"}))) + ["INSERT INTO device (name) VALUES (?) ON DUPLICATE KEY UPDATE name = ?" + "Printer" "Printer"])) + (is (= (sql/format (-> (insert-into :device) + (columns :id :name) + (values [[4 "Printer"]]) + (on-duplicate-key-update {:name "Central Printer"}))) + ["INSERT INTO device (id, name) VALUES (?, ?) ON DUPLICATE KEY UPDATE name = ?" + 4 "Printer" "Central Printer"])) + (is (= (sql/format (-> (insert-into :table) + (columns :c1) + (values [[42]]) + (on-duplicate-key-update {:c1 [:+ [:values :c1] 1]}))) + ["INSERT INTO table (c1) VALUES (?) ON DUPLICATE KEY UPDATE c1 = VALUES(c1) + ?" + 42 1])))) \ No newline at end of file