Fork me on GitHub

I was interested in making a generic upsert function in hugsql. It felt a little gross, but I wound up with this:

-- :name upserting! :insert :returning-execute
-- :doc Upserts multiple records, handling updates on conflict
-- :require [clojure.string :as str]
insert into :identifier:table (:identifier*:cols) values :tuple*:vals
(let [update-cols (:update-cols params)
      multi-update? (> (count update-cols) 1)
      format-excluded (fn [cols] (->> cols
                                      (map (fn [s] (str "excluded." s)))
                                      (str/join ", ")))
      on-conflict " on conflict (:identifier*:conflict-cols) do "]
  (case (:action params)
    :nothing (str on-conflict "nothing")
    :update (str on-conflict
                 "update set "
                 (if multi-update?
                   (str "(:identifier*:update-cols)" " = " "(" (format-excluded update-cols) ")")
                   (str ":identifier*:update-cols"   " = " (format-excluded update-cols))))
If anyone is searching in the future: ON CONFLICT DO UPDATE SET Usage looks like this:
(upserting! @db {:table "bogus"
                 :cols ["id" "foo" "bar"]
                 :vals [[1 2 99]
                        [2 3 42]
                        [4 4 1000]]
                 :conflict-cols ["id"]
                 :update-cols ["foo" "bar"]
                 :action :update})
where :action can be :nothing or :update. If anyone has any suggestions on cleaning this up, I'd be interested to hear them.


Also, is there a way I'm missing that will convert keys to strings so for instance, :table is :bogus in the calling fn?


Another question I had while tinkering: What if I want to tell a user that they haven't supplied the required parameters? For instance in the above, :action is required. I tried asserting above the insert piece, but it gives me an execution error and says SQL is empty. I suppose that's better than nothing, but it would be nice if I could communicate a better error message.