Share your thoughts in the 2021 Clojure Community Survey!

Welcome! Please see the About page for a little more info on how this works.

0 votes
in Libs by
edited by

What is the prefered way to realize row from the result of next.jdbc/plan as is, without any transformation?

Identity doesn't work:

(require '[next.jdbc :as jdbc])

(def sql-params
  ["SELECT column1
      FROM (VALUES (1, 1, 1),
                   (2, 2, 2),
                   (3, 3, 3)) AS _"])

(into []
      (map identity)
      (jdbc/plan db-spec
                 sql-params))
;=> [{row} from `plan` -- missing `map` or `reduce`?
;    {row} from `plan` -- missing `map` or `reduce`?
;    {row} from `plan` -- missing `map` or `reduce`?]

If rows are represented as maps (default), #(into {} %) works:

(into []
      (map #(into {} %))
      (jdbc/plan db-spec
                 sql-params))
;=> [{:column1 1} {:column1 2} {:column1 3}]

If rows are represented as vectors (builder-fn), vec can be used:

(require '[next.jdbc.result-set :as rs])

(into []
      (map vec)
      (jdbc/plan db-spec
                 sql-params
                 {:builder-fn rs/as-arrays}))
;=> [[1] [2] [3]]

Is there any xform that would cover both map and vector row realization?

Or should I use something other than plan for lazy result set reading?

1 Answer

+1 vote
by
selected by
 
Best answer

If you just want the entire result set as a vector of hash maps, use execute!.

If you need to stream a (large) result set lazily from the database, you must use plan.

If you want the rows as actual hash maps when reducing a plan, you have two choices:

  1. Call rs/datafiable-row on each row to fully realize that row as Clojure data and then process it,
  2. Call select-keys on each row with a list of columns you need.

If you perform any action on a row that requires it to be realized as a Clojure data structure, then rs/datafiable-row will automatically be called. (keys row) will call seq under the hood and that will force full realization.

select-keys will not realize the row because individual entries can be pulled from the underlying ResultSet directly (.entryAt is called since the ResultSet is wrapped in Associative). You can also get individual columns by name without realizing the whole row ((:col row) or (get row :row) both work).

by
Can you please provide a code example of using `rs/datafiable-row`? I can't find one on the github page... Not sure how to use it with `plan` because it has 3 arg arity.
by
Just as you call (jdbc/plan connectable sql-params opts), you call (rs/datafiable-row row connectable opts)

So you would (map #(rs/datafiable-row % db-spec opts)) in your (into [] ...) reduction.

This seems to be an area where the docs could be improved :)
by
The latest documentation (for the 1.0.384 release) includes more information about datafiable-row and has an example.
by
Great, thanks!
...