Is it possible to set a default :builder-fn in next.jdbc so I don't have to specify it with every execute call?
No, but you can replace direct calls to next.jdbc with a wrapper in your projects that provides all the necessary functionality in a central place.
The closest you can get is to put :builder-fn in your db-spec hash map and/or use with-options https://cljdoc.org/d/com.github.seancorfield/next.jdbc/1.3.1002/api/next.jdbc#with-options
This is part of the deliberate discouragement of using a non-default builder ๐
Why is it considered bad?
I'll turn that around: why do you want to use a non-default builder?
To specify as-unqualified-modified-maps absolutely everywhere. :)
I also have a custom builder that preprocesses each row in a specific way before it becomes a part of the result. So basically like the default builder but with (row! [_ row] (next.jdbc.result-set/row! builder (transform row))).
Maybe use plan more and execute! less? ๐
For the latter? Sure, that would make sense. But it's some old code that got translated from the old jdbc with minimal changes, so not too keen on changing it now.
I want the keys to use hรญfens, that's all. To prevent people forgetting to use underscores when using maps that came from a select.
why do you want to use a non-default builder?my two cents: personally I find it quite inconvenient then the column name is profile_id and I get it as :user/profile-id . One should always keep in mind this mapping. Thus, I always specify as-unqualified-lower-maps : what you see in PGAdmin is what you get in Clojure.
Also, some databases like Postgres perform an extra query to get table names for namespaces.
@seancorfield Following up from the issue raised by @jrychter.
> The closest you can get is to put :builder-fn in your db-spec hash map
Can you give an example? I couldn't find any in the docs, couldn't make it work myself.
My bad... that's something that works with clojure.java.jdbc -- but you need with-options for in next.jdbc. I misremembered how db-spec maps were handled.
In c.j.j, everything took a hash map, and the hash map contained :datasource or :connection but could contain arbitrary options that were merged with the explicit options in each call.
In next.jdbc, you're generally dealing with DataSource or Connection objects, so the only way to get "default" options in calls is via the with-options machinery (and for transactions, you then need to use with-transaction+options.
Right, makes sense.
with-transaction+options also doesn't work because only a very limited subset of options is supported. In fact, :builder-fn in there leads to an exception.
Really? That doesn't sound right...
From the docstring:
The options map supports:
* `:isolation` -- `:none`, `:read-committed`, `:read-uncommitted`,
`:repeatable-read`, `:serializable`,
* `:read-only` -- `true` / `false` (`true` will make the `Connection` readonly),
* `:rollback-only` -- `true` / `false` (`true` will make the transaction
rollback, even if it would otherwise succeed).
The actual result:
(let [db-src {:jdbcUrl "jdbc:[...]"
:options {:builder-fn (fn [rs opts]
(
rs
(assoc opts :qualifier-fn unquote-ident
:label-fn unquote-ident)))}}]
(jdbc/with-transaction+options [c db-src]
(jdbc/execute-one! c
(sql/format {:select [[1 :Library-Ref]]}
{:dialect :spaced-out}))))
Execution error (PSQLException) at org.postgresql.core.v3.QueryExecutorImpl/receiveErrorResponse (QueryExecutorImpl.java:2733).
FATAL: invalid command-line argument for server process: {:builder-fn
Hint: Try "postgres --help" for more information.
Specifying :builder-fn at the top-level of the db-src map has no effect as only the :options key is read by with-transaction+options.That's not how you use it.
(jdbc/with-transaction+options [ds' (jdbc/with-options ds-opts jdbc/snake-kebab-opts)]
(is (= (merge (default-options) jdbc/snake-kebab-opts)
(:options ds')))
(is (= "red" ((col-kw :fruit/looks-like)
(jdbc/execute-one!
ds'
[(str "select appearance as looks_like from fruit where " (index) " = ?") 1])))))You have to use with-options and then you can use with-transaction+options to unwrap, create a TX on a Connection, and then re-wrap it.
You can't put options in the db-spec map except for a few specific things that affect the initial creation of a Connection.
Ohhh, of course.
Why did it not make sense to make with-transaction always respect options explicitly created by with-options?
Because it traffics in plain Connection objects.
i.e., it guarantees that you can call JDBC methods directly on the bound symbol.
I see, thanks.
The error you get, BTW, is because :options ends up getting passed as part of a Properties object to the JDBC driver -- and the value of :options is stringified.