Next.jdbc -- early access

#21

The current performance-focused route is via reducible!, for processing large result sets. That doesn’t create a hash map at all if you only use operations that lookup keys.

The other two scenarios that are goals of the library are fetch a single, fully-realized row and fetch a fully-realized sequence of rows. I’ve been talking to Ghadi about various options for allowing extensible row-building. If I go that route, it could produce hash maps with qualified keys, arrays of column names/row values, or anything else you want – including records – but qualified keywords will be the default choice because I believe that is the “right” default for most Clojure apps, especially those that also use spec.

I’d be interested in seeing what you have in mind for a separation of “compile” and “execute” (but not as a PR – and I’ll remind everyone of https://github.com/seancorfield/next-jdbc/blob/master/CONTRIBUTING.md at this point).

2 Likes
#22

Sorry, didn’t notice the CONTRIBUTING. Pushed my ideas with tests into https://github.com/metosin/porsas. Hopefully some of the work can be joined later.

2 Likes
#23

I’ve reworked next.jdbc to include RowBuilder and ResultSetBuilder inspired by discussions with Ghadi. I’ve added an example of building Fruit records with a custom builder.

I’ve also moved the “sugar” functions to next.jdbc.sql, deleted execute! and execute-one! from result-set and reimplemented them as part of the Executable protocol so they no longer go through the reducible! path (which speeds them up quite a bit).

I’m not sure how much further I want to pursue performance at this point, given the flexibility I still want users of the library to have (and I am not moving to a “precompile”/“execute”, beyond what you can do with creating a PreparedStatement and then executing/reducing it).

Feedback is welcome but I’m getting close to the point where I want to decide on a permanent home and make a release…

3 Likes
#24

See Seancorfield/next.jdbc "1.0.0-alpha8"

2 Likes
#25

I don’t understand why do you need datafy at all, why not just return navigables that return other navigables? Your use of datafy seems like a no-op, and since datafy works on everything (returning identity by default), just returning navigables will do the same with fewer hops.

Another thing that slightly worries me is that datafy in this case is basically a memory leak: you keep a reference to a connection that is useful only during development (probably?).

#26

For now, datafy is (almost) a no-op in this case – it takes a row in a result set and makes it navigable. The idiom is Thing -> datafy -> (navigable) data -> nav -> new Thing – that’s just how the protocols work and what REBL etc expect. Could I just make rows Navigable and rely on datafy being a no-op on them always? Maybe, but making them truly Datafiable feels like the “right thing to do” based on the intended idiomatic usage. The datafy behavior could be changed/enhanced down the line and this way we already have a hook for that.

As for the memory leak, the reference isn’t going to be any longer-lived than the data it is attached to – and the recommendation for next.jdbc is to pass around a connectable such as a Datasource which would be much longer-lived anyway (if you’re using a connection pooled datasource, that’s what you’d be passing to most operations anyway).

It is one of the caveats of using REBL with this, that if you do pass a Connection instead, and it gets .closed before REBL calls nav, you’ll get an error anyway.

1 Like
#27

Hello, I got this very confusing

ClassCastException/CompilerException “java.lang.Character cannot be cast to java.lang.String” at “prepare.clj” 82

When I forgot to put the SQL into a vector:

(let [my-datasource (get-datasource "jdbc:h2:tcp://localhost:1521/opt/h2-data/mb;MODE=Oracle;USER=sa")]
    (with-open [connection (get-connection my-datasource)]
      (execute-one! connection "SELECT ENABLED FROM organization"))) ; <- ERR

I realize this is alpha lib but anyway, perhaps something to put on the todo list, to
provide better error message here?

Thanks for the library, BTW!

#28

I think it should be (execute-one! connection ["SELECT ENABLED FROM organization"]).

Edit: the question was about error messages. Could the functions allow both strings and vectors?

#29

No, I’m not making that mistake again https://stuartsierra.com/2015/06/10/clojure-donts-heisenparameter

I did it in clojure.java.jdbc and it propagated conditional logic through a lot of the library (since the API was so wide) and it made the specs harder to write and it also impacts performance: everyone pays the price just so some people can omit [ .. ] around SQL statements that have no parameters.

next.jdbc's API is much narrower so it wouldn’t be as bad but it was a deliberate decision not to do this.

#30

Options are:

  • Add an assert
  • Add a :pre condition
  • Add an explicit conditional
  • Provide specs and let developers instrument code in development if they want

The first three all incur a cost for everyone (the first two could be turned off for production but I don’t know how many people really do that) so I don’t want to go down that path.

The fourth is opt-in so I am more inclined to do that. If you had specs for next.jdbc, would that address your request @HolyJak?

#31

Good points. And yes, it would (providing the Getting started guide mentioned it and demonstrated how to turn them on.

#32
1 Like