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)]
(mapv (fn [^Integer i]
(keyword (when-let [qualifier (not-empty (.getTableName rsmeta i))]
(ident-fn qualifier))
(ident-fn (.getColumnLabel rsmeta i))))
idxs)
(mapv (fn [^Integer i]
(keyword (not-empty (.getTableName rsmeta i))
(.getColumnLabel rsmeta i))) (.getColumnLabel rsmeta i)))
idxs)))) (range 1 (inc (.getColumnCount rsmeta)))))
(defn get-unqualified-column-names
"Given ResultSetMetaData, return a vector of unqualified column names."
[^ResultSetMetaData rsmeta opts]
(mapv (fn [^Integer i] (keyword (.getColumnLabel rsmeta i)))
(range 1 (inc (.getColumnCount rsmeta)))))
(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,29 +77,7 @@
(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
bare hash map rows."
[^ResultSet rs opts]
(let [rsmeta (.getMetaData rs)
cols (get-column-names rsmeta opts)]
(reify
RowBuilder
(->row [this] (transient {}))
(column-count [this] (count cols))
(with-column [this row i]
(assoc! row
(nth cols (dec i))
(read-column-by-index (.getObject rs ^Integer i) rsmeta i)))
(row! [this row] (persistent! row)))))
(defn map-rs-builder
"Given a ResultSet and options, return a RowBuilder and ResultSetBuilder
that produces bare vectors of hash map rows."
[^ResultSet rs opts]
(let [rsmeta (.getMetaData rs)
cols (get-column-names rsmeta opts)]
(reify
RowBuilder RowBuilder
(->row [this] (transient {})) (->row [this] (transient {}))
(column-count [this] (count cols)) (column-count [this] (count cols))
@ -115,17 +88,27 @@
(row! [this row] (persistent! row)) (row! [this row] (persistent! row))
ResultSetBuilder ResultSetBuilder
(->rs [this] (transient [])) (->rs [this] (transient []))
(with-row [this rs row] (with-row [this mrs row]
(conj! rs row)) (conj! mrs row))
(rs! [this rs] (persistent! rs))))) (rs! [this mrs] (persistent! mrs)))
(defn as-arrays (defn as-maps
"Given a ResulSet and options, return a RowBuilder and ResultSetBuilder "Given a ResultSet and options, return a RowBuilder and ResultSetBuilder
that produces a vector of column names followed by vectors of row values." 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)))
(defn as-unqualified-maps
"Given a ResultSet and options, return a RowBuilder and ResultSetBuilder
that produces bare vectors of hash map rows, with simple keys."
[^ResultSet rs opts]
(let [rsmeta (.getMetaData rs)
cols (get-unqualified-column-names rsmeta opts)]
(->MapResultSetBuilder rs rsmeta cols)))
(defrecord ArrayResultSetBuilder [^ResultSet rs rsmeta cols]
RowBuilder RowBuilder
(->row [this] (transient [])) (->row [this] (transient []))
(column-count [this] (count cols)) (column-count [this] (count cols))
@ -134,9 +117,26 @@
(row! [this row] (persistent! row)) (row! [this row] (persistent! row))
ResultSetBuilder ResultSetBuilder
(->rs [this] (transient [cols])) (->rs [this] (transient [cols]))
(with-row [this rs row] (with-row [this ars row]
(conj! rs row)) (conj! ars row))
(rs! [this rs] (persistent! rs))))) (rs! [this ars] (persistent! ars)))
(defn as-arrays
"Given a ResulSet and options, return a RowBuilder and ResultSetBuilder
that produces a vector of column names followed by vectors of row values."
[^ResultSet rs opts]
(let [rsmeta (.getMetaData rs)
cols (get-column-names rsmeta opts)]
(->ArrayResultSetBuilder rs rsmeta cols)))
(defn as-unqualified-arrays
"Given a ResulSet and options, return a RowBuilder and ResultSetBuilder
that produces a vector of simple column names followed by vectors of row
values."
[^ResultSet rs opts]
(let [rsmeta (.getMetaData rs)
cols (get-unqualified-column-names rsmeta opts)]
(->ArrayResultSetBuilder rs rsmeta cols)))
(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)]