Fork me on GitHub
#sql
<
2021-06-16
>
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?

seancorfield16:06:35

@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

3
seancorfield16:06:38

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 https://github.com/seancorfield/next-jdbc/blob/develop/test/next/jdbc/result_set_test.clj#L382

seancorfield17:06:54

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

seancorfield17:06:29

(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]
       (reify
         rs/RowBuilder
         (~'->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/ResultSetBuilder
         (~'->rs [_#] (transient []))
         (~'with-row [_# ~'rs row#] (conj! ~rs row#))
         (~'rs! [_# ~'rs] (persistent! ~rs))
         clojure.lang.ILookup
         (~'valAt [this# k#] (get this# k# nil))
         (~'valAt [this# k# not-found#]
           (case k#
             :cols ~(mapv keyword fields)
             :rsmeta ~rsmeta
             not-found#))))))

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

seancorfield17:06:29

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

seancorfield18:06:20

My commiserations 🙂

Ben Sless18:06:32

I've seen things you would not believe 🙂

3