seancorfield / next-jdbc

A modern low-level Clojure wrapper for JDBC-based access to databases.
https://cljdoc.org/d/com.github.seancorfield/next.jdbc/
Eclipse Public License 1.0
755 stars 90 forks source link

Support something like a `:reverse-label-fn` for fetching values a column without using a row builder with a key that doesn't exactly match the column name #221

Closed camsaul closed 1 year ago

camsaul commented 1 year ago

As discussed here: https://clojurians.slack.com/archives/C1Q164V29/p1662494291800529

Say I have a table with snake_case keys and I'm fetching results with something like as-unqualified-kebab-maps that has a :label-fn that transforms the results to kebab-case. If I fetch an entire row, I would get

{:id 1, :name "Cam", :updated-at "2022-09-06T12:47:00-08:00"}

but if I just wanted to fetch values of :updated-at I can't do something like

(with-open [rs ...]
  (into [] (map :updated-at) (jdbc.rs/reducible-result-set rs {:builder-fn jdbc.rs/as-unqualified-kebab-maps})))

because it never uses the result map builder at all... I would have to in this case just know that the column is actually updated_at and write my map transducer using that key instead.

My suggestion was to support some sort of option like :reverse-label-fn (default clojure.core/name) and use that places in mapify-result-set like entryAt instead of calling name directly, e.g.

(let [builder (delay ((get opts :builder-fn next.jdbc.rs/as-maps) rs opts))
      name-fn (get opts :reverse-label-fn name)]
  (reify
    next.jdbc.result_set.MapifiedResultSet
    ...
    (entryAt [this k]
      (try
        (clojure.lang.MapEntry. k (next.jdbc.rs/read-column-by-label
                                   (.getObject rs (name-fn k))
                                   (name-fn k)))
        (catch SQLException _)))
    ...))

I think this would actually increase performance a bit since you would avoid repeated var dereferences of #'clojure.core/name, but either way it would be completely negligible compared to the cost of actually running the query, especially when your DB isn't in-memory H2.

seancorfield commented 1 year ago

For consistency with other options in next.jdbc, this should probably be :column-fn since it will apply to column names going from Clojure to SQL.