Fork me on GitHub
#sql
<
2022-11-17
>
plins18:11:54

noob, question, probably more related to clojure than next-jdbc itself but Im trying to doseq the results of jdbc/plan to write each row into a stream but Im getting Don't know how to create ISeq from: next.jdbc.result_set$eval32556$fn$reify__32558 as an error should I do anything first, before trying to iterate the collection? I dont want to realize the entire sequence into memory, but fetch and write the row into the stream one at a time (so Im only interested in the collateral effect itself)

seancorfield18:11:05

plan is intended to be used with reduce or transduce or similar -- it produces a reducible collection, but doesn't actually do anything until you reduce it.

seancorfield18:11:12

See the examples in the documentation.

plins19:11:10

what Im failing to understand is: is it possible to iterate through the results of a plan (just to do IO, I dont actually need to reduce anything) without loading the entire collection into memory? maybe plan is not right function to use? my use case is simple, I want to iterate through the results of plan and write each row into a stream, without loading the entire collection in memory maybe there is a better function to achieve this?

seancorfield19:11:48

You have to use reduce. (or similar).

seancorfield19:11:01

plan's "result" is a reducible collection.

seancorfield19:11:41

execute! produces a vector with the entire result set in it. If you want to "stream" a result set, you have to use plan and reduce.

plins19:11:34

alright, reduce should do it, thanks a lot for the detailed explanation 🙂

seancorfield19:11:04

There is no lazy (Clojure) interface to the data in next.jdbc -- deliberately 🙂

seancorfield19:11:40

You'll also want to read the Tips & Tricks section of the docs for your database for any hints around how to get the JDBC driver to stream result sets properly -- it's DB-specific, unfortunately.

thanks3 1
seancorfield19:11:04

(MySQL and PostgreSQL have documentation there for streaming result sets)

plins20:11:02

is it possible to get the column names of a result-set before reducing it? or should I reduce the first element and figure out manually?

plins20:11:22

naively tried to execute (jdbc.result-set/get-unqualified-lower-column-names (.getMetaData plan-result) {}) but it could not find the method

seancorfield20:11:47

As I keep saying, the result of plan is a reducible collection -- the only thing you can do with it is reduce it.

seancorfield20:11:49

Inside the reducing function, you can call column-names on the "row" abstraction. You can also call metadata on it. In general, with plan, you can only operate on the "row" abstraction (which is a thin map-like wrapper over the underlying mutable ResultSet), inside the reducing function.

seancorfield20:11:06

Also, inside the reducing function, there is no notion of qualified column names -- they are column labels. There is no hash map (in the Clojure sense). The "row" is a view onto the ResultSet object, so (:foo row) fetches the column labeled "foo" from the current ResultSet's row.

plins20:11:09

got it! thanks for the clarifications again!

seancorfield21:11:16

Thinking about this some more, there's kind of a big gap between the "easy" stuff with next.jdbc -- execute! and the "friendly SQL functions" which all traffic in Clojure data structures -- and the "simple" but more advanced stuff in the library, where you need to have more of an understanding of the underlying JDBC stuff as well as Clojure stuff like reducible collections/reducing functions/abstractions/protocols/etc. If you have any suggestions for how to provide a "bridge" between those in the documentation, that would have made it easier for you to figure out how to solve your initial problem, I'd be happy to update/expand the docs! I'm a bit too familiar with all that to really have a good feel for what needs additional explanation in the next.jdbc docs as opposed to pointing folks at more JDBC docs and/or more Clojure docs.

❤️ 2
plins16:11:43

I think the major problem for me was I never used the reduce fn to do side fx, and it took me a while to understand that whatever I do it should be inside of a reduce also the confusion with lazyness

seancorfield17:11:22

Hmm, maybe I should add some examples to the docs using run! for when you want to process a plan for side effects and throw away the results? run! is literally a reduce expression that throws away the result:

build=> (source run!)
(defn run!
  "Runs the supplied procedure (via reduce), for purposes of side
  effects, on successive items in the collection. Returns nil"
  {:added "1.7"}
  [proc coll]
  (reduce #(proc %2) nil coll)
  nil)

plins17:11:03

yeah that might help, it just felt wrong using reduce for that, and it turns out run! is more suitable for my needs thanks for pointing out its existence