Fork me on GitHub
Mark Mac16:06:53

I want to reset an Oracle expired password programmatically, which I can achieve using interop directly against java.sql.DriverManager, by calling DriverManager/getConnection and supplying a Properties "info" argument which contains the Oracle specific connection property "oracle.jdbc.newPassword". Is it possible to do this using next.jdbc?


@mark926 In theory, you can just add :oracle.jdbc.newPassword "newsecret" to your db-spec hash map and when you first call get-connection, that will happen.

Mark Mac17:06:19

That worked, thought I'd tried that - but obv hadn't got it quite right - thanks


When next.jdbc processes a db-spec hash map, it extracts the parts it needs to build the JDBC URL itself and then builds a Properties object with the rest of the hash map and passes it to the driver when creating that connection.

Ben Sless17:06:08

Is there a way to conveniently generate a record builder given a record type? There's probably some reflection-heavy way but I'd rather not go there if I can avoid it A way to generalize this


I’d ask: why would you want to do that?


(but there’s always map->MyRecord available for every record type which you could map over the result set if you really must have records instead of hash maps)

Ben Sless17:06:34

Don't get me wrong, I don't really want to do that 🙂 It's a temporary measure meant to fix a bad design choice in a service which shouldn't even be using an in-memory sql db. I hope I can improve its performance that way, assuming building records instead of maps will be both faster and less GC intensive.

Ben Sless17:06:45

which means I would ideally like to avoid the map->Record constructor

Ben Sless17:06:47

Just ended up wrapping defrecord to emit the constructor

(defn emit-builder
  [name fields]
  (let [builder (symbol (str "->" name "Builder"))
        rs (with-meta 'rs {:tag (.getName ResultSet)})
        rsmeta (with-meta 'rsmeta {:tag (.getName ResultSetMetaData)})]
    `(defn ~builder [~rs ~rsmeta]
         (~'->row [_#] (new ~name ~@(for [f (map str fields)] `(.getObject ~rs ~f))))
         (~'column-count [_#] 0)
         (~'with-column [_# row# _#] row#)
         (~'with-column-value [_# row# _# _#] row#)
         (~'row! [_# row#] row#)
         (~'->rs [_#] (transient []))
         (~'with-row [_# ~'rs row#] (conj! ~rs row#))
         (~'rs! [_# ~'rs] (persistent! ~rs))
         (~'valAt [this# k#] (get this# k# nil))
         (~'valAt [this# k# not-found#]
           (case k#
             :cols ~(mapv keyword fields)
             :rsmeta ~rsmeta

(defmacro defsqlrecord
  [name fields & opts]
  (let [builder (symbol (str "->" name "Builder"))
        adapter (symbol (str name "Adapter"))]
       (defrecord ~name ~fields ~@opts)
       ~(emit-builder name fields)
       (def ~adapter (next-adapter/hugsql-adapter-next-jdbc
                      {:builder-fn ~builder}))


Not building a result set is going to be faster — if you can use plan and reduce.

Ben Sless17:06:47

true, that will be the next step, but it will require a more involved solution


My commiserations 🙂

Ben Sless18:06:32

I've seen things you would not believe 🙂