honeysql

2025-07-27T00:45:42.005129Z

2025-07-27T16:27:34.014389Z

DrLj贸tsson 2025-07-27T07:50:40.498429Z

Would it be possible to make the automatic conversion of namespaced keywords into table-qualified columns optional? Details in 馃У

DrLj贸tsson 2025-07-27T07:51:14.501889Z

I am not using next.jdbc, and want to be able to specify the namespace of column keys in my query. For example {:select [[:a :ns/a]] :from [:b]} would become "SELECT a` AS ns/a FROM b` instead of "SELECT a` AS ns.`a` FROM b` (which is actually illegal syntax in MySQL)

DrLj贸tsson 2025-07-27T07:53:04.372979Z

I use clojure.java.jdbc, which will gracefully return :ns/a as the column name for a column named "ns/a" in the result set.

DrLj贸tsson 2025-07-27T07:54:29.457719Z

From the docs "Namespace-qualified keywords (and symbols) are generally treated as table-qualified columns: :foo/bar becomes foo.bar"

seancorfield 2025-07-27T14:59:03.105269Z

For this specific example -- with ns/a as an alias -- you can use a string:

user=> (sql/format {:select [[:a "ns/a"]] :from [:b]})
["SELECT a AS \"ns/a\" FROM b"]
Since you are (presumably) using MySQL, the :nrql dialect might work for you:
user=> (sql/format {:select [:ns/a] :from [:b]} :dialect :nrql)
["SELECT `ns/a` FROM b"]
You won't get MySQL's SET priority change for UPDATE but you will get literal column names quoted as you want them.

DrLj贸tsson 2025-07-27T16:31:00.631579Z

Using a string does not work in for example HAVING

(hsql/format {:select [[:a "ns/a"]]
                :from [:foo]
                :having [:= "ns/a" "bar"]}
               {:dialect :mysql})

seancorfield 2025-07-27T16:32:17.278719Z

String only works for an alias, i.e., in select. It does not work for general column names.

seancorfield 2025-07-27T16:33:17.357689Z

Another option is the quoted-keyword syntax:

user=> (sql/format {:select [:'ns/a] :from [:b] :having [:= :'ns/a :b]})
["SELECT ns/a FROM b HAVING ns/a = b"]

seancorfield 2025-07-27T16:33:54.454279Z

But that will not quote the columns (so it'll be invalid MySQL syntax I suspect):

user=> (sql/format {:select [:'ns/a] :from [:b] :having [:= :'ns/a :b]} {:quoted true})
["SELECT ns/a FROM \"b\" HAVING ns/a = \"b\""]

seancorfield 2025-07-27T16:35:45.872739Z

You could register a new, custom dialect that is a combination of these, if :nrql doesn't work for you as-is (due to the SET priority):

:mysql     {:quote #(strop "`" % "`")
                           :clause-order-fn
                           #(add-clause-before % :set :where)}
               :nrql      {:quote    #(strop "`" % "`")
                           :col-fn   #(if (keyword? %) (subs (str %) 1) (str %))
                           :parts-fn vector}

DrLj贸tsson 2025-07-27T16:36:35.181309Z

I'm trying out the new dialect option as we type 馃檪

DrLj贸tsson 2025-07-27T16:38:11.217339Z

Something like this?

(hsql/register-dialect! :mysql2 (assoc (get @@#'hsql/dialects :mysql)
                                           :col-fn #(if (keyword? %) (subs (str %) 1) (str %))
                                           :parts-fn vector))
    (hsql/format {:select [[:a :ns/a]]
                  :from [:foo]
                  :having [:= "bar" :ns/a]}
                 {:dialect :mysql2})

DrLj贸tsson 2025-07-27T16:38:56.169749Z

["SELECT a` AS ns/a FROM foo HAVING ? = ns/a" "bar"]`

seancorfield 2025-07-27T16:39:18.306969Z

But I'm not changing how a/b works in general and I'm not adding yet another new option to override that -- it's too deeply baked in to how HoneySQL (and next.jdbc) works. c.j.j is deprecated at this point, essentially, and hasn't had an update in over four years.

seancorfield 2025-07-27T16:40:12.898859Z

Yup, that's a reasonable dialect variant.

DrLj贸tsson 2025-07-27T16:41:43.228839Z

Fair enough. Can you foresee any edge cases with the :mysql2 solution? I notice that the default :parts-fn closes over aliased , what is aliased?

DrLj贸tsson 2025-07-27T16:41:57.052519Z

parts-fn    (or (:parts-fn *dialect*)
                        #(if-let [n (when-not (or drop-ns (string? e))
                                      (namespace-_ e))]
                           [n %]
                           (if aliased
                             [%]
                             (str/split % #"\."))))

seancorfield 2025-07-27T16:42:43.723049Z

That's the "loophole" that allows for aliases to be strings and not split at . in that one specific context. Edited: (string? e) is what skips the / splitting.

seancorfield 2025-07-27T16:43:03.014049Z

Well, more than that. But that's one of the side-effects.

DrLj贸tsson 2025-07-27T16:43:15.868679Z

Aha, so that one would be covered then.

DrLj贸tsson 2025-07-27T16:44:01.430329Z

Oh, drop-ns is also closed over

seancorfield 2025-07-27T16:44:29.429749Z

Yeah, there's some complex edge cases involved... which is why I don't want to change it any further... 馃槥

seancorfield 2025-07-27T16:46:02.687879Z

IIRC, :drop-ns is so that :table/col becomes just col in the context of DDL column names (which cannot be qualified as table.col).

馃憤 1
seancorfield 2025-07-27T16:46:37.218509Z

It also covers SET expressions (`SET table.col = ?` is not valid, but SET col = ? is).

DrLj贸tsson 2025-07-27T16:46:45.959609Z

Fully understand. I know that I should move on to next.jdbc but the whole table as namespace thing doesn't fit well. I'm working with a legacy database and need to "change" table names and assign namespaces to calculated columns.

seancorfield 2025-07-27T16:46:59.884089Z

Sorry 馃槥

seancorfield 2025-07-27T16:47:55.642289Z

This was a big problem with XTDB v2 when they started moving from a Clojure/Datalog API to a SQL API -- and they ended up switching from / to $ internally for column names as I recall.

DrLj贸tsson 2025-07-27T16:48:42.573859Z

No need to be sorry - I love honeysql! I've only had to work around this specific thing with namespaces. I've found a very hacky solution but wanted to see if it was possible to do it in a cleaner way

DrLj贸tsson 2025-07-27T16:51:59.797059Z

I'm going to run my tests against the modified dialect option and see what happens.

馃憤馃徎 1
DrLj贸tsson 2025-07-27T17:59:41.981429Z

Darn, didn't work.

(hsql/format {:select [[:a :ns/a]]
                  :from [:foo]
                  :where [:= :a.b 9]
                  :having [:= :ns/a "bar"]}
                 {:dialect :mysql2})
;=> ["SELECT `a` AS `ns/a` FROM `foo` WHERE `a.b` = ? HAVING `ns/a` = ?" 9 "bar"]
a.b`` instead of a`.`b``

DrLj贸tsson 2025-07-27T18:39:13.111699Z

With this all my tests pass (without using my previous workaround)

(hsql/register-dialect! :mysql2 (assoc (get @@#'hsql/dialects :mysql)
                                       :col-fn (fn [entity]
                                                 (let [x (cond
                                                           (and (keyword? entity) (namespace entity))
                                                           (subs (str entity) 1)
                                                           (keyword? entity)
                                                           (name entity)
                                                           :else
                                                           entity)]
                                                   x))
                                       :parts-fn (fn [entity] (str/split entity #"\."))))
But it would have been nice to have access to the closed over drop-ns and aliased ...

seancorfield 2025-07-27T19:06:12.240329Z

I think you can simplify that to (if (keyword? entity) (subs (str entity) 1) entity) ? And you should be to omit :parts-fn altogether I think?

DrLj贸tsson 2025-07-27T19:13:50.912529Z

Yes, the :col-fn can of course be simplified. But I need the :parts-fn because the default extracts the namespace

seancorfield 2025-07-27T19:25:29.932689Z

Ah, OK. I'm not at my main computer right now to check. Feel free to create an issue describing all of this and maybe I can clean it up and make it a viable dialect that has the subtleties of drop-ns and aliased as well. This might be a nice opportunity to refactor and expose some "mix'n'match" functions for folks to build more dialects...

鉂わ笍 1
DrLj贸tsson 2025-07-27T20:46:27.746049Z

I am officially on vacation but till try to get around to it within 1-20 days 馃槉

馃憤馃徎 1