Hey team, is there a way I can include a sql comment with honeysql? Context: I generate a pretty involved list of CTEs, from simpler input arguments. Here's a simplified example:
(defn sql-pretty [sql-str]
(-> (SqlFormatter/of "PostgreSql")
(.format sql-str)))
(defn hsql-pretty [x]
(-> x
hsql/format
first
sql-pretty
println))
(defn make-query [inputs]
{:with (map
(fn [input] [input {:select :* :from :input}])
inputs)
:select :* :from (last inputs)})
(hsql-pretty
(make-query [:foo :bar]))
; (out) WITH foo AS (
; (out) SELECT
; (out) *
; (out) FROM
; (out) input
; (out) ),
; (out) bar AS (
; (out) SELECT
; (out) *
; (out) FROM
; (out) input
; (out) )
; (out) SELECT
; (out) *
; (out) FROM
; (out) bar
For each WITH clause, I would love to include a pr-str of the input argument which caused it. For example:
WITH /* :foo */ foo AS (
) ...
Is there some way I can insert the comment /* :foo */, when writing the query?
I naively tried something like this:
(comment
(defn make-query [inputs]
{:with (map
(fn [input] [[:raw (str "/* " (pr-str input) " */ " (name input))]
{:select :* :from :input}])
inputs)
:select :* :from (last inputs)})
(hsql-pretty
(make-query [:foo :bar])))
But got:
#error {
:cause "class java.lang.Character cannot be cast to class java.util.Map$Entry (java.lang.Character and java.util.Map$Entry are in module java.base of loader 'bootstrap')"
:via
[{:type java.lang.ClassCastException
:message "class java.lang.Character cannot be cast to class java.util.Map$Entry (java.lang.Character and java.util.Map$Entry are in module java.base of loader 'bootstrap')"
:at [clojure.lang.APersistentMap$KeySeq first "APersistentMap.java" 171]}]
For my own information - what's the purpose of those comments?
I sometimes need to debug slow queries, which can get really large (~500 LOC). The actual inputs are much smaller, so as I go through the CTEs, I would love to see the inputs that generated them
But /* :foo */ doesn't let you see the inputs. The full HoneySQL map does. So you'd have to manually map /* :foo */ in SQL to :foo in Clojure. And at that point, why is it better than mapping foo to :foo?
The example is a bit contrived, but in reality looks something like this:
input: [?user :groups "group-id"]
CTE:
WITH match_0_1 AS (
SELECT
entity_id AS match_0_0_entity_id,
attr_id AS match_0_0_attr_id,
value AS match_0_0_value_blob,
CASE
WHEN eav THEN CAST(value ->> 0 AS UUID)
ELSE null
END AS match_0_0_value_uuid,
created_at AS match_0_0_created_at
FROM
triples
WHERE
(
app_id = 'a749930e-6737-4dcf-b039-60c7f5e4e2e6' :: uuid
)
AND (vae = true)
AND (
attr_id = 'a15d4213-a976-493a-b88b-b88bd6f2f324' :: uuid
)
AND (
(
value = CAST('"15d0dbcf-e80d-4706-a179-5649035380b4"' AS JSONB)
)
)
)
I would love it to look like:
-- [?user :groups "group-id"]
WITH match_0_1 AS (
SELECT
entity_id AS match_0_0_entity_id,
attr_id AS match_0_0_attr_id,
value AS match_0_0_value_blob,
CASE
WHEN eav THEN CAST(value ->> 0 AS UUID)
ELSE null
END AS match_0_0_value_uuid,
created_at AS match_0_0_created_at
FROM
triples
WHERE
(
app_id = 'a7491230e-6727-4dcf-b039-60c7f5e4e2e6' :: uuid
)
AND (vae = true)
AND (
attr_id = 'a15234213-a176-493a-b88b-b88bd6f2f324' :: uuid
)
AND (
(
value = CAST('"15d0dbcf-e80d-6706-a179-5649035380b4"' AS JSONB)
)
)
)
Ah, I see.
Not sure if it's something that could be described as "officially supported", but it somewhat works:
(let [inputs [:foo :bar]]
(sql/format
{:with (map
(fn [input] [[input {:raw (str "/* " input " */")}]
{:select :* :from :input}])
inputs)
:select :* :from (last inputs)}))tl;dr: use :raw expression with earmuffs, that's the simplest approach. You can extend honeysql with a :comment clause that produces /*...*/ when rendering.
@p-himik in rare cases, comments might help. various ORM systems put additional data into comments for investigation, for example the name of controller, request-id, host-id, etc.
I log SQL queries separately from the RDBMS, so I put those there.
But I get that the comments could be useful. It's just logging :foo when foo is already there that confused me.