Rewrite builders as records

This seems to be very slightly faster than using `reify`. Also rename 
the map builder to `as-maps` and add unqualified variants of builders.
This commit is contained in:
Sean Corfield 2019-04-18 08:12:12 -07:00
parent 90a4b7d727
commit e5ea6af959

View file

@ -21,23 +21,18 @@
(set! *warn-on-reflection* true) (set! *warn-on-reflection* true)
(defn get-column-names (defn get-column-names
"Given ResultSetMetaData, return a vector of columns names, each qualified by "Given ResultSetMetaData, return a vector of column names, each qualified by
the table from which it came. the table from which it came."
If :identifiers was specified, apply that to both the table qualifier
and the column name."
[^ResultSetMetaData rsmeta opts] [^ResultSetMetaData rsmeta opts]
(let [idxs (range 1 (inc (.getColumnCount rsmeta)))] (mapv (fn [^Integer i] (keyword (not-empty (.getTableName rsmeta i))
(if-let [ident-fn (:identifiers opts)] (.getColumnLabel rsmeta i)))
(mapv (fn [^Integer i] (range 1 (inc (.getColumnCount rsmeta)))))
(keyword (when-let [qualifier (not-empty (.getTableName rsmeta i))]
(ident-fn qualifier)) (defn get-unqualified-column-names
(ident-fn (.getColumnLabel rsmeta i)))) "Given ResultSetMetaData, return a vector of unqualified column names."
idxs) [^ResultSetMetaData rsmeta opts]
(mapv (fn [^Integer i] (mapv (fn [^Integer i] (keyword (.getColumnLabel rsmeta i)))
(keyword (not-empty (.getTableName rsmeta i)) (range 1 (inc (.getColumnCount rsmeta)))))
(.getColumnLabel rsmeta i)))
idxs))))
(defprotocol ReadableColumn (defprotocol ReadableColumn
"Protocol for reading objects from the java.sql.ResultSet. Default "Protocol for reading objects from the java.sql.ResultSet. Default
@ -82,42 +77,49 @@
(with-row [_ rs row]) (with-row [_ rs row])
(rs! [_ rs])) (rs! [_ rs]))
(defn map-row-builder (defrecord MapResultSetBuilder [^ResultSet rs rsmeta cols]
"Given a ResultSet and options, return a RowBuilder that produces RowBuilder
bare hash map rows." (->row [this] (transient {}))
[^ResultSet rs opts] (column-count [this] (count cols))
(let [rsmeta (.getMetaData rs) (with-column [this row i]
cols (get-column-names rsmeta opts)] (assoc! row
(reify (nth cols (dec i))
RowBuilder (read-column-by-index (.getObject rs ^Integer i) rsmeta i)))
(->row [this] (transient {})) (row! [this row] (persistent! row))
(column-count [this] (count cols)) ResultSetBuilder
(with-column [this row i] (->rs [this] (transient []))
(assoc! row (with-row [this mrs row]
(nth cols (dec i)) (conj! mrs row))
(read-column-by-index (.getObject rs ^Integer i) rsmeta i))) (rs! [this mrs] (persistent! mrs)))
(row! [this row] (persistent! row)))))
(defn map-rs-builder (defn as-maps
"Given a ResultSet and options, return a RowBuilder and ResultSetBuilder "Given a ResultSet and options, return a RowBuilder and ResultSetBuilder
that produces bare vectors of hash map rows." that produces bare vectors of hash map rows."
[^ResultSet rs opts] [^ResultSet rs opts]
(let [rsmeta (.getMetaData rs) (let [rsmeta (.getMetaData rs)
cols (get-column-names rsmeta opts)] cols (get-column-names rsmeta opts)]
(reify (->MapResultSetBuilder rs rsmeta cols)))
RowBuilder
(->row [this] (transient {})) (defn as-unqualified-maps
(column-count [this] (count cols)) "Given a ResultSet and options, return a RowBuilder and ResultSetBuilder
(with-column [this row i] that produces bare vectors of hash map rows, with simple keys."
(assoc! row [^ResultSet rs opts]
(nth cols (dec i)) (let [rsmeta (.getMetaData rs)
(read-column-by-index (.getObject rs ^Integer i) rsmeta i))) cols (get-unqualified-column-names rsmeta opts)]
(row! [this row] (persistent! row)) (->MapResultSetBuilder rs rsmeta cols)))
ResultSetBuilder
(->rs [this] (transient [])) (defrecord ArrayResultSetBuilder [^ResultSet rs rsmeta cols]
(with-row [this rs row] RowBuilder
(conj! rs row)) (->row [this] (transient []))
(rs! [this rs] (persistent! rs))))) (column-count [this] (count cols))
(with-column [this row i]
(conj! row (read-column-by-index (.getObject rs ^Integer i) rsmeta i)))
(row! [this row] (persistent! row))
ResultSetBuilder
(->rs [this] (transient [cols]))
(with-row [this ars row]
(conj! ars row))
(rs! [this ars] (persistent! ars)))
(defn as-arrays (defn as-arrays
"Given a ResulSet and options, return a RowBuilder and ResultSetBuilder "Given a ResulSet and options, return a RowBuilder and ResultSetBuilder
@ -125,18 +127,16 @@
[^ResultSet rs opts] [^ResultSet rs opts]
(let [rsmeta (.getMetaData rs) (let [rsmeta (.getMetaData rs)
cols (get-column-names rsmeta opts)] cols (get-column-names rsmeta opts)]
(reify (->ArrayResultSetBuilder rs rsmeta cols)))
RowBuilder
(->row [this] (transient [])) (defn as-unqualified-arrays
(column-count [this] (count cols)) "Given a ResulSet and options, return a RowBuilder and ResultSetBuilder
(with-column [this row i] that produces a vector of simple column names followed by vectors of row
(conj! row (read-column-by-index (.getObject rs ^Integer i) rsmeta i))) values."
(row! [this row] (persistent! row)) [^ResultSet rs opts]
ResultSetBuilder (let [rsmeta (.getMetaData rs)
(->rs [this] (transient [cols])) cols (get-unqualified-column-names rsmeta opts)]
(with-row [this rs row] (->ArrayResultSetBuilder rs rsmeta cols)))
(conj! rs row))
(rs! [this rs] (persistent! rs)))))
(declare navize-row) (declare navize-row)
@ -166,7 +166,7 @@
Supports Seqable which realizes a full row of the data." Supports Seqable which realizes a full row of the data."
[^ResultSet rs opts] [^ResultSet rs opts]
(let [gen (delay ((get :gen-fn opts map-row-builder) rs opts))] (let [gen (delay ((get :gen-fn opts as-maps) rs opts))]
(reify (reify
clojure.lang.ILookup clojure.lang.ILookup
@ -208,11 +208,12 @@
(extend-protocol (extend-protocol
DatafiableRow DatafiableRow
clojure.lang.IObj java.util.Map ; assume we can "navigate" any kind of hash map for now
(datafiable-row [this connectable opts] (datafiable-row [this connectable opts]
(with-meta (with-meta this
this {`core-p/datafy (navize-row connectable opts)}))
{`core-p/datafy (navize-row connectable opts)}))) clojure.lang.IObj ; but we cannot "navigate" other things
(datafiable-row [this connectable opts] this))
(defn- stmt->result-set (defn- stmt->result-set
"Given a PreparedStatement and options, execute it and return a ResultSet "Given a PreparedStatement and options, execute it and return a ResultSet
@ -262,7 +263,7 @@
(rest sql-params) (rest sql-params)
opts)] opts)]
(if-let [rs (stmt->result-set stmt opts)] (if-let [rs (stmt->result-set stmt opts)]
(let [gen-fn (get opts :gen-fn map-row-builder) (let [gen-fn (get opts :gen-fn as-maps)
gen (gen-fn rs opts)] gen (gen-fn rs opts)]
(when (.next rs) (when (.next rs)
(datafiable-row (row-builder gen) this opts))) (datafiable-row (row-builder gen) this opts)))
@ -273,7 +274,7 @@
(rest sql-params) (rest sql-params)
opts)] opts)]
(if-let [rs (stmt->result-set stmt opts)] (if-let [rs (stmt->result-set stmt opts)]
(let [gen-fn (get opts :gen-fn map-rs-builder) (let [gen-fn (get opts :gen-fn as-maps)
gen (gen-fn rs opts)] gen (gen-fn rs opts)]
(loop [rs' (->rs gen) more? (.next rs)] (loop [rs' (->rs gen) more? (.next rs)]
(if more? (if more?
@ -301,7 +302,7 @@
opts)] opts)]
(if-let [rs (stmt->result-set stmt opts)] (if-let [rs (stmt->result-set stmt opts)]
(when (.next rs) (when (.next rs)
(datafiable-row (row-builder (map-row-builder rs opts)) this opts)) (datafiable-row (row-builder (as-maps rs opts)) this opts))
{:next.jdbc/update-count (.getUpdateCount stmt)})))) {:next.jdbc/update-count (.getUpdateCount stmt)}))))
(-execute-all [this sql-params opts] (-execute-all [this sql-params opts]
(with-open [con (p/get-connection this opts)] (with-open [con (p/get-connection this opts)]
@ -310,7 +311,7 @@
(rest sql-params) (rest sql-params)
opts)] opts)]
(if-let [rs (stmt->result-set stmt opts)] (if-let [rs (stmt->result-set stmt opts)]
(let [gen-fn (get opts :gen-fn map-rs-builder) (let [gen-fn (get opts :gen-fn as-maps)
gen (gen-fn rs opts)] gen (gen-fn rs opts)]
(loop [rs' (->rs gen) more? (.next rs)] (loop [rs' (->rs gen) more? (.next rs)]
(if more? (if more?
@ -331,12 +332,12 @@
(-execute-one [this _ opts] (-execute-one [this _ opts]
(if-let [rs (stmt->result-set this (assoc opts :return-keys true))] (if-let [rs (stmt->result-set this (assoc opts :return-keys true))]
(when (.next rs) (when (.next rs)
(datafiable-row (row-builder (map-row-builder rs opts)) (datafiable-row (row-builder (as-maps rs opts))
(.getConnection this) opts)) (.getConnection this) opts))
{:next.jdbc/update-count (.getUpdateCount this)})) {:next.jdbc/update-count (.getUpdateCount this)}))
(-execute-all [this sql-params opts] (-execute-all [this sql-params opts]
(if-let [rs (stmt->result-set this opts)] (if-let [rs (stmt->result-set this opts)]
(let [gen-fn (get opts :gen-fn map-rs-builder) (let [gen-fn (get opts :gen-fn as-maps)
gen (gen-fn rs opts)] gen (gen-fn rs opts)]
(loop [rs' (->rs gen) more? (.next rs)] (loop [rs' (->rs gen) more? (.next rs)]
(if more? (if more?
@ -379,7 +380,7 @@
in the <table> portion of their name, the key is called 'id', and the in the <table> portion of their name, the key is called 'id', and the
cardinality is :one. cardinality is :one.
Rows are looked up using 'execute!' or 'execute-one!' and the :entities Rows are looked up using 'execute!' or 'execute-one!' and the :table-fn
function, if provided, is applied to both the assumed table name and the function, if provided, is applied to both the assumed table name and the
assumed foreign key column name." assumed foreign key column name."
[connectable opts] [connectable opts]
@ -390,7 +391,7 @@
(default-schema k))] (default-schema k))]
(if fk (if fk
(try (try
(let [entity-fn (:entities opts identity) (let [entity-fn (:table-fn opts identity)
exec-fn! (if (= :many cardinality) exec-fn! (if (= :many cardinality)
p/-execute-all p/-execute-all
p/-execute-one)] p/-execute-one)]