Building a MERGE query
Since MERGE is not yet supported by HoneySQL, and we need that in our project, therefore I am trying to build that using the available structures. I almost succeeded, with simple values my implementation works, but it fails with JSON values.
One part of the query gets built using {:insert-into …} but I cannot get keyword parameters in.
Does anybody know why this isn’t working?
(-> {:insert-into [:table :t], :using [[{:values [:?value :?key]} [:s [:composite :value :key]]]], :where [:and [:= :t/key :s/key]]}
(hsql/format {:params {:foo "FOO"}}))@ikaraszi Have you written a merge formatter that you register?
No, I didn’t do that, I used the existing functions to create a MERGE statement for my use-case. I could have dig deeper, but I needed this fast
So it is a hack of using the existing formatters and then replace some parts of the statement
I don’t think that is usable for you in its current form 😞
Ah, interesting. If you get a chance to write up what you ended doing in a GH issue for HoneySQL, I would still be interested, since I'm not familiar with the MERGE clause and it seems to have "interesting" syntax which is partly why I haven't implemented it yet...
This is the top-level build function that I came up with:
(defn build [table set-params where-params]
{:pre [(keyword? table) (valid-params? set-params) (valid-params? where-params)]}
(let [all-params (merge set-params where-params)
[insert-into-stmnt & params] (-> {:insert-into [table :t]
:using [[(build-source table) :s]]
:where (table+source [:and] [:=] (keys where-params))}
(hsql/format {:params {:all all-params}}))
when-stmnt (build-when (keys all-params) (keys set-params))]
(-> insert-into-stmnt
(str/replace #"^INSERT INTO" "MERGE INTO")
(str/replace " WHERE " " ON ")
(str " " when-stmnt)
vector
(into params))))(There's an open GH issue about it)
And build-when is a version of build-where?
(defn build-when [insert-columns update-columns]
(let [diff-conditions (table+source [:or] [:is-distinct-from] update-columns)
insert-stmnt (build-insert insert-columns)
update-stmnt (build-update update-columns)]
(-> [:case
[:not :MATCHED] [:raw insert-stmnt]
[:and :MATCHED diff-conditions] [:raw update-stmnt]]
hsql/format
first
(str/replace #"^CASE " "")
(str/replace #" END$" ""))))(defn build-insert [columns]
(-> {:insert-into :table
:columns columns
:values (->> columns
(mapv (fn [column] (qualified-column :s column)))
vector)}
hsql/format
first
(str/replace #"^INSERT INTO table" "INSERT")))
(defn build-update [columns]
(-> {:update :table
:set (table+source {} columns)}
hsql/format
first
(str/replace #"^UPDATE table" "UPDATE")))As you can see it’s quite hacky
Still helpful tho', thank you.
I am glad if you find it useful
See https://github.com/seancorfield/honeysql/issues/499 if you have any more thoughts...
I would change the API of my fn to simply pass a set-params and a unique-fields instead of expecting to maps
I believe a vector is missing
What does the desired result look like?
(-> {:insert-into [:table :t], :using [[{:values [[:?value :?key]]} [:s [:composite :value :key]]]], :where [:and [:= :t/key :s/key]]}
(hsql/format {:params {:value "VALUE" :key "KEY"}}))this works
It does work after I added the missing vector wrapping