First cut at RowBuilder
Temporarily disables as-arrays
This commit is contained in:
parent
eefea54358
commit
788015909f
3 changed files with 94 additions and 59 deletions
|
|
@ -148,9 +148,9 @@
|
||||||
([stmt]
|
([stmt]
|
||||||
(rs/execute! stmt [] (partial into {}) {}))
|
(rs/execute! stmt [] (partial into {}) {}))
|
||||||
([connectable sql-params]
|
([connectable sql-params]
|
||||||
(rs/execute! connectable sql-params (rs/datafiable-row connectable {}) {}))
|
(rs/execute! connectable sql-params #(rs/datafiable-row % connectable {}) {}))
|
||||||
([connectable sql-params opts]
|
([connectable sql-params opts]
|
||||||
(rs/execute! connectable sql-params (rs/datafiable-row connectable opts) opts))
|
(rs/execute! connectable sql-params #(rs/datafiable-row % connectable opts) opts))
|
||||||
([connectable sql-params f opts]
|
([connectable sql-params f opts]
|
||||||
(rs/execute! connectable sql-params f opts)))
|
(rs/execute! connectable sql-params f opts)))
|
||||||
|
|
||||||
|
|
@ -167,9 +167,9 @@
|
||||||
([stmt]
|
([stmt]
|
||||||
(rs/execute-one! stmt [] (partial into {}) {}))
|
(rs/execute-one! stmt [] (partial into {}) {}))
|
||||||
([connectable sql-params]
|
([connectable sql-params]
|
||||||
(rs/execute-one! connectable sql-params (rs/datafiable-row connectable {}) {}))
|
(rs/execute-one! connectable sql-params #(rs/datafiable-row % connectable {}) {}))
|
||||||
([connectable sql-params opts]
|
([connectable sql-params opts]
|
||||||
(rs/execute-one! connectable sql-params (rs/datafiable-row connectable opts) opts))
|
(rs/execute-one! connectable sql-params #(rs/datafiable-row % connectable opts) opts))
|
||||||
([connectable sql-params f opts]
|
([connectable sql-params f opts]
|
||||||
(rs/execute-one! connectable sql-params f opts)))
|
(rs/execute-one! connectable sql-params f opts)))
|
||||||
|
|
||||||
|
|
@ -240,7 +240,7 @@
|
||||||
opts)
|
opts)
|
||||||
(rs/execute! connectable
|
(rs/execute! connectable
|
||||||
sql-params
|
sql-params
|
||||||
(rs/datafiable-row connectable opts)
|
#(rs/datafiable-row % connectable opts)
|
||||||
opts))))
|
opts))))
|
||||||
|
|
||||||
(defn find-by-keys
|
(defn find-by-keys
|
||||||
|
|
@ -258,7 +258,7 @@
|
||||||
opts)
|
opts)
|
||||||
(rs/execute! connectable
|
(rs/execute! connectable
|
||||||
(sql/for-query table key-map opts)
|
(sql/for-query table key-map opts)
|
||||||
(rs/datafiable-row connectable opts)
|
#(rs/datafiable-row % connectable opts)
|
||||||
opts))))
|
opts))))
|
||||||
|
|
||||||
(defn get-by-id
|
(defn get-by-id
|
||||||
|
|
@ -281,7 +281,7 @@
|
||||||
opts)
|
opts)
|
||||||
(rs/execute-one! connectable
|
(rs/execute-one! connectable
|
||||||
(sql/for-query table {pk-name pk} opts)
|
(sql/for-query table {pk-name pk} opts)
|
||||||
(rs/datafiable-row connectable opts)
|
#(rs/datafiable-row % connectable opts)
|
||||||
opts))))
|
opts))))
|
||||||
|
|
||||||
(defn update!
|
(defn update!
|
||||||
|
|
|
||||||
|
|
@ -12,12 +12,12 @@
|
||||||
(set! *warn-on-reflection* true)
|
(set! *warn-on-reflection* true)
|
||||||
|
|
||||||
(defn- get-column-names
|
(defn- get-column-names
|
||||||
"Given a ResultSet, return a vector of columns names, each qualified by
|
"Given ResultSetMetaData, return a vector of columns 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
|
If :identifiers was specified, apply that to both the table qualifier
|
||||||
and the column name."
|
and the column name."
|
||||||
[^ResultSet rs ^ResultSetMetaData rsmeta opts]
|
[^ResultSetMetaData rsmeta opts]
|
||||||
(let [idxs (range 1 (inc (.getColumnCount rsmeta)))]
|
(let [idxs (range 1 (inc (.getColumnCount rsmeta)))]
|
||||||
(if-let [ident-fn (:identifiers opts)]
|
(if-let [ident-fn (:identifiers opts)]
|
||||||
(mapv (fn [^Integer i]
|
(mapv (fn [^Integer i]
|
||||||
|
|
@ -30,12 +30,6 @@
|
||||||
(.getColumnLabel rsmeta i)))
|
(.getColumnLabel rsmeta i)))
|
||||||
idxs))))
|
idxs))))
|
||||||
|
|
||||||
(defprotocol ColumnarResultSet
|
|
||||||
"To allow reducing functions to access a result set's column names and
|
|
||||||
row values, such as the as-arrays reducing function in this namespace."
|
|
||||||
(column-names [this] "Return the column names from a result set.")
|
|
||||||
(row-values [this] "Return the values from the current row of a result set."))
|
|
||||||
|
|
||||||
(defprotocol ReadableColumn
|
(defprotocol ReadableColumn
|
||||||
"Protocol for reading objects from the java.sql.ResultSet. Default
|
"Protocol for reading objects from the java.sql.ResultSet. Default
|
||||||
implementations (for Object and nil) return the argument, and the
|
implementations (for Object and nil) return the argument, and the
|
||||||
|
|
@ -59,6 +53,37 @@
|
||||||
(read-column-by-label [_1 _2] nil)
|
(read-column-by-label [_1 _2] nil)
|
||||||
(read-column-by-index [_1 _2 _3] nil))
|
(read-column-by-index [_1 _2 _3] nil))
|
||||||
|
|
||||||
|
(defprotocol RowBuilder
|
||||||
|
"Protocol for building rows in various representations:
|
||||||
|
row -- called once per row to create the basis of each row
|
||||||
|
columns -- return the number of columns in each row
|
||||||
|
with-column -- called with the index of the column to be added
|
||||||
|
build -- called once per row to finalize each row once it is complete"
|
||||||
|
(row [_])
|
||||||
|
(columns [_])
|
||||||
|
(with-column [_ row i])
|
||||||
|
(build [_ row]))
|
||||||
|
|
||||||
|
(defn map-row-builder [^ResultSet rs opts]
|
||||||
|
(let [rsmeta (.getMetaData rs)
|
||||||
|
cols (get-column-names rsmeta opts)]
|
||||||
|
(reify
|
||||||
|
RowBuilder
|
||||||
|
(row [this] (transient {}))
|
||||||
|
(columns [this] (count cols))
|
||||||
|
(with-column [this row i]
|
||||||
|
(assoc! row
|
||||||
|
(nth cols (dec i))
|
||||||
|
(read-column-by-index (.getObject rs ^Integer i) rsmeta i)))
|
||||||
|
(build [this row] (persistent! row)))))
|
||||||
|
|
||||||
|
(declare navize-row)
|
||||||
|
|
||||||
|
(defprotocol DatafiableRow
|
||||||
|
"Given a connectable object, return a function that knows how to turn a row
|
||||||
|
into a datafiable object that can be 'nav'igated."
|
||||||
|
(datafiable-row [this connectable opts]))
|
||||||
|
|
||||||
(defn- mapify-result-set
|
(defn- mapify-result-set
|
||||||
"Given a result set, return an object that wraps the current row as a hash
|
"Given a result set, return an object that wraps the current row as a hash
|
||||||
map. Note that a result set is mutable and the current row will change behind
|
map. Note that a result set is mutable and the current row will change behind
|
||||||
|
|
@ -71,8 +96,12 @@
|
||||||
|
|
||||||
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 [rsmeta (.getMetaData rs)
|
(let [gen (when-let [gen-fn (:gen-fn opts)] (gen-fn rs opts))
|
||||||
cols (delay (get-column-names rs rsmeta opts))]
|
row-builder (fn [] ; blows up if :gen-fn not provided
|
||||||
|
(->> (reduce (fn [r i] (with-column gen r i))
|
||||||
|
(row gen)
|
||||||
|
(range 1 (inc (columns gen))))
|
||||||
|
(build gen)))]
|
||||||
(reify
|
(reify
|
||||||
|
|
||||||
clojure.lang.ILookup
|
clojure.lang.ILookup
|
||||||
|
|
@ -100,26 +129,27 @@
|
||||||
(name k)))
|
(name k)))
|
||||||
(catch SQLException _)))
|
(catch SQLException _)))
|
||||||
(assoc [this k v]
|
(assoc [this k v]
|
||||||
(assoc (into {} (seq this)) k v))
|
(assoc (row-builder) k v))
|
||||||
|
|
||||||
clojure.lang.Seqable
|
clojure.lang.Seqable
|
||||||
(seq [this]
|
(seq [this]
|
||||||
(seq (mapv (fn [^Integer i]
|
(seq (row-builder)))
|
||||||
(clojure.lang.MapEntry. (nth @cols (dec i))
|
|
||||||
(read-column-by-index
|
|
||||||
(.getObject rs i)
|
|
||||||
rsmeta i)))
|
|
||||||
(range 1 (inc (count @cols))))))
|
|
||||||
|
|
||||||
ColumnarResultSet
|
DatafiableRow
|
||||||
(column-names [this] @cols)
|
(datafiable-row [this connectable opts]
|
||||||
(row-values [this]
|
(with-meta
|
||||||
(mapv (fn [^Integer i] (read-column-by-index
|
(row-builder)
|
||||||
(.getObject rs i)
|
{`core-p/datafy (navize-row connectable opts)})))))
|
||||||
rsmeta i))
|
|
||||||
(range 1 (inc (count @cols))))))))
|
|
||||||
|
|
||||||
(defn as-arrays
|
(extend-protocol
|
||||||
|
DatafiableRow
|
||||||
|
clojure.lang.IObj
|
||||||
|
(datafiable-row [this connectable opts]
|
||||||
|
(with-meta
|
||||||
|
this
|
||||||
|
{`core-p/datafy (navize-row connectable opts)})))
|
||||||
|
|
||||||
|
#_(defn as-arrays
|
||||||
"A reducing function that can be used on a result set to produce an
|
"A reducing function that can be used on a result set to produce an
|
||||||
array-based representation, where the first element is a vector of the
|
array-based representation, where the first element is a vector of the
|
||||||
column names in the result set, and subsequent elements are vectors of
|
column names in the result set, and subsequent elements are vectors of
|
||||||
|
|
@ -190,15 +220,6 @@
|
||||||
(-execute [this sql-params opts]
|
(-execute [this sql-params opts]
|
||||||
(p/-execute (p/get-datasource this) sql-params opts)))
|
(p/-execute (p/get-datasource this) sql-params opts)))
|
||||||
|
|
||||||
(declare navize-row)
|
|
||||||
|
|
||||||
(defn datafiable-row
|
|
||||||
"Given a connectable object, return a function that knows how to turn a row
|
|
||||||
into a datafiable object that can be 'nav'igated."
|
|
||||||
[connectable opts]
|
|
||||||
(fn [row]
|
|
||||||
(into (with-meta {} {`core-p/datafy (navize-row connectable opts)}) row)))
|
|
||||||
|
|
||||||
(defn execute!
|
(defn execute!
|
||||||
"Given a connectable object and SQL and parameters, execute it and reduce it
|
"Given a connectable object and SQL and parameters, execute it and reduce it
|
||||||
into a vector of processed hash maps (rows).
|
into a vector of processed hash maps (rows).
|
||||||
|
|
@ -207,7 +228,9 @@
|
||||||
[connectable sql-params f opts]
|
[connectable sql-params f opts]
|
||||||
(into []
|
(into []
|
||||||
(map f)
|
(map f)
|
||||||
(p/-execute connectable sql-params opts)))
|
(p/-execute connectable
|
||||||
|
sql-params
|
||||||
|
(update opts :gen-fn #(or % map-row-builder)))))
|
||||||
|
|
||||||
(defn execute-one!
|
(defn execute-one!
|
||||||
"Given a connectable object and SQL and parameters, execute it and return
|
"Given a connectable object and SQL and parameters, execute it and return
|
||||||
|
|
@ -217,7 +240,9 @@
|
||||||
[connectable sql-params f opts]
|
[connectable sql-params f opts]
|
||||||
(reduce (fn [_ row] (reduced (f row)))
|
(reduce (fn [_ row] (reduced (f row)))
|
||||||
nil
|
nil
|
||||||
(p/-execute connectable sql-params opts)))
|
(p/-execute connectable
|
||||||
|
sql-params
|
||||||
|
(update opts :gen-fn #(or % map-row-builder)))))
|
||||||
|
|
||||||
(defn- default-schema
|
(defn- default-schema
|
||||||
"The default schema lookup rule for column names.
|
"The default schema lookup rule for column names.
|
||||||
|
|
@ -265,7 +290,7 @@
|
||||||
(entity-fn (name fk))
|
(entity-fn (name fk))
|
||||||
" = ?")
|
" = ?")
|
||||||
v]
|
v]
|
||||||
(datafiable-row connectable opts)
|
#(datafiable-row % connectable opts)
|
||||||
opts))
|
opts))
|
||||||
(catch Exception _
|
(catch Exception _
|
||||||
;; assume an exception means we just cannot
|
;; assume an exception means we just cannot
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,12 @@
|
||||||
["select * from fruit where appearance = ?" "red"]
|
["select * from fruit where appearance = ?" "red"]
|
||||||
:name
|
:name
|
||||||
{}))
|
{}))
|
||||||
|
(quick-bench
|
||||||
|
(execute-one! con
|
||||||
|
["select * from fruit where appearance = ?" "red"]
|
||||||
|
:name ; turn off row builder as we don't need it
|
||||||
|
;; this gives a very slight improvement
|
||||||
|
{:gen-fn (constantly nil)}))
|
||||||
;; 5.7 micros -- 3.7x
|
;; 5.7 micros -- 3.7x
|
||||||
(quick-bench
|
(quick-bench
|
||||||
(jdbc/query {:connection con}
|
(jdbc/query {:connection con}
|
||||||
|
|
@ -79,10 +85,12 @@
|
||||||
(execute! con ["select * from fruit"]))
|
(execute! con ["select * from fruit"]))
|
||||||
;; this is not quite equivalent
|
;; this is not quite equivalent
|
||||||
(quick-bench ; 5.34-5.4
|
(quick-bench ; 5.34-5.4
|
||||||
(into [] (map (partial into {})) (reducible! con ["select * from fruit"])))
|
(into [] (map (partial into {})) (reducible! con ["select * from fruit"]
|
||||||
|
{:gen-fn rs/map-row-builder})))
|
||||||
;; but this is (equivalent to execute!)
|
;; but this is (equivalent to execute!)
|
||||||
(quick-bench ; 5.58-5.8
|
(quick-bench ; 5.58-5.8
|
||||||
(into [] (map (rs/datafiable-row con {})) (reducible! con ["select * from fruit"])))
|
(into [] (map #(rs/datafiable-row % con {})) (reducible! con ["select * from fruit"]
|
||||||
|
{:gen-fn rs/map-row-builder})))
|
||||||
|
|
||||||
(quick-bench ; 7.84-7.96 -- 1.3x
|
(quick-bench ; 7.84-7.96 -- 1.3x
|
||||||
(jdbc/query {:connection con} ["select * from fruit"]))
|
(jdbc/query {:connection con} ["select * from fruit"]))
|
||||||
|
|
@ -137,16 +145,18 @@
|
||||||
(execute-one! con ["select * from fruit where appearance = ?" "red"]))
|
(execute-one! con ["select * from fruit where appearance = ?" "red"]))
|
||||||
|
|
||||||
;; test assoc works
|
;; test assoc works
|
||||||
|
(quick-bench
|
||||||
(execute-one! con
|
(execute-one! con
|
||||||
["select * from fruit where appearance = ?" "red"]
|
["select * from fruit where appearance = ?" "red"]
|
||||||
#(assoc % :test :value)
|
#(assoc % :test :value)
|
||||||
{})
|
{}))
|
||||||
|
|
||||||
;; test assoc works
|
;; test assoc works
|
||||||
|
(quick-bench
|
||||||
(execute! con
|
(execute! con
|
||||||
["select * from fruit where appearance = ?" "red"]
|
["select * from fruit where appearance = ?" "red"]
|
||||||
#(assoc % :test :value)
|
#(assoc % :test :value)
|
||||||
{})
|
{}))
|
||||||
|
|
||||||
(with-transaction [t con {:rollback-only true}]
|
(with-transaction [t con {:rollback-only true}]
|
||||||
(insert! t :fruit {:id 5, :name "Pear", :appearance "green", :cost 49, :grade 47})
|
(insert! t :fruit {:id 5, :name "Pear", :appearance "green", :cost 49, :grade 47})
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue