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]
|
||||
(rs/execute! stmt [] (partial into {}) {}))
|
||||
([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]
|
||||
(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]
|
||||
(rs/execute! connectable sql-params f opts)))
|
||||
|
||||
|
|
@ -167,9 +167,9 @@
|
|||
([stmt]
|
||||
(rs/execute-one! stmt [] (partial into {}) {}))
|
||||
([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]
|
||||
(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]
|
||||
(rs/execute-one! connectable sql-params f opts)))
|
||||
|
||||
|
|
@ -240,7 +240,7 @@
|
|||
opts)
|
||||
(rs/execute! connectable
|
||||
sql-params
|
||||
(rs/datafiable-row connectable opts)
|
||||
#(rs/datafiable-row % connectable opts)
|
||||
opts))))
|
||||
|
||||
(defn find-by-keys
|
||||
|
|
@ -258,7 +258,7 @@
|
|||
opts)
|
||||
(rs/execute! connectable
|
||||
(sql/for-query table key-map opts)
|
||||
(rs/datafiable-row connectable opts)
|
||||
#(rs/datafiable-row % connectable opts)
|
||||
opts))))
|
||||
|
||||
(defn get-by-id
|
||||
|
|
@ -281,7 +281,7 @@
|
|||
opts)
|
||||
(rs/execute-one! connectable
|
||||
(sql/for-query table {pk-name pk} opts)
|
||||
(rs/datafiable-row connectable opts)
|
||||
#(rs/datafiable-row % connectable opts)
|
||||
opts))))
|
||||
|
||||
(defn update!
|
||||
|
|
|
|||
|
|
@ -12,12 +12,12 @@
|
|||
(set! *warn-on-reflection* true)
|
||||
|
||||
(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.
|
||||
|
||||
If :identifiers was specified, apply that to both the table qualifier
|
||||
and the column name."
|
||||
[^ResultSet rs ^ResultSetMetaData rsmeta opts]
|
||||
[^ResultSetMetaData rsmeta opts]
|
||||
(let [idxs (range 1 (inc (.getColumnCount rsmeta)))]
|
||||
(if-let [ident-fn (:identifiers opts)]
|
||||
(mapv (fn [^Integer i]
|
||||
|
|
@ -30,12 +30,6 @@
|
|||
(.getColumnLabel rsmeta i)))
|
||||
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
|
||||
"Protocol for reading objects from the java.sql.ResultSet. Default
|
||||
implementations (for Object and nil) return the argument, and the
|
||||
|
|
@ -59,6 +53,37 @@
|
|||
(read-column-by-label [_1 _2] 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
|
||||
"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
|
||||
|
|
@ -71,8 +96,12 @@
|
|||
|
||||
Supports Seqable which realizes a full row of the data."
|
||||
[^ResultSet rs opts]
|
||||
(let [rsmeta (.getMetaData rs)
|
||||
cols (delay (get-column-names rs rsmeta opts))]
|
||||
(let [gen (when-let [gen-fn (:gen-fn opts)] (gen-fn rs 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
|
||||
|
||||
clojure.lang.ILookup
|
||||
|
|
@ -100,27 +129,28 @@
|
|||
(name k)))
|
||||
(catch SQLException _)))
|
||||
(assoc [this k v]
|
||||
(assoc (into {} (seq this)) k v))
|
||||
(assoc (row-builder) k v))
|
||||
|
||||
clojure.lang.Seqable
|
||||
(seq [this]
|
||||
(seq (mapv (fn [^Integer i]
|
||||
(clojure.lang.MapEntry. (nth @cols (dec i))
|
||||
(read-column-by-index
|
||||
(.getObject rs i)
|
||||
rsmeta i)))
|
||||
(range 1 (inc (count @cols))))))
|
||||
(seq (row-builder)))
|
||||
|
||||
ColumnarResultSet
|
||||
(column-names [this] @cols)
|
||||
(row-values [this]
|
||||
(mapv (fn [^Integer i] (read-column-by-index
|
||||
(.getObject rs i)
|
||||
rsmeta i))
|
||||
(range 1 (inc (count @cols))))))))
|
||||
DatafiableRow
|
||||
(datafiable-row [this connectable opts]
|
||||
(with-meta
|
||||
(row-builder)
|
||||
{`core-p/datafy (navize-row connectable opts)})))))
|
||||
|
||||
(defn as-arrays
|
||||
"A reducing function that can be used on a result set to produce an
|
||||
(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
|
||||
array-based representation, where the first element is a vector of the
|
||||
column names in the result set, and subsequent elements are vectors of
|
||||
the rows from the result set.
|
||||
|
|
@ -128,10 +158,10 @@
|
|||
It should be used with a nil initial value:
|
||||
|
||||
(reduce rs/as-arrays nil (reducible! con sql-params))"
|
||||
[result rs-map]
|
||||
(if result
|
||||
(conj result (row-values rs-map))
|
||||
(conj [(column-names rs-map)] (row-values rs-map))))
|
||||
[result rs-map]
|
||||
(if result
|
||||
(conj result (row-values rs-map))
|
||||
(conj [(column-names rs-map)] (row-values rs-map))))
|
||||
|
||||
(defn- reduce-stmt
|
||||
"Execute the PreparedStatement, attempt to get either its ResultSet or
|
||||
|
|
@ -190,15 +220,6 @@
|
|||
(-execute [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!
|
||||
"Given a connectable object and SQL and parameters, execute it and reduce it
|
||||
into a vector of processed hash maps (rows).
|
||||
|
|
@ -207,7 +228,9 @@
|
|||
[connectable sql-params f opts]
|
||||
(into []
|
||||
(map f)
|
||||
(p/-execute connectable sql-params opts)))
|
||||
(p/-execute connectable
|
||||
sql-params
|
||||
(update opts :gen-fn #(or % map-row-builder)))))
|
||||
|
||||
(defn execute-one!
|
||||
"Given a connectable object and SQL and parameters, execute it and return
|
||||
|
|
@ -217,7 +240,9 @@
|
|||
[connectable sql-params f opts]
|
||||
(reduce (fn [_ row] (reduced (f row)))
|
||||
nil
|
||||
(p/-execute connectable sql-params opts)))
|
||||
(p/-execute connectable
|
||||
sql-params
|
||||
(update opts :gen-fn #(or % map-row-builder)))))
|
||||
|
||||
(defn- default-schema
|
||||
"The default schema lookup rule for column names.
|
||||
|
|
@ -265,7 +290,7 @@
|
|||
(entity-fn (name fk))
|
||||
" = ?")
|
||||
v]
|
||||
(datafiable-row connectable opts)
|
||||
#(datafiable-row % connectable opts)
|
||||
opts))
|
||||
(catch Exception _
|
||||
;; assume an exception means we just cannot
|
||||
|
|
|
|||
|
|
@ -61,6 +61,12 @@
|
|||
["select * from fruit where appearance = ?" "red"]
|
||||
: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
|
||||
(quick-bench
|
||||
(jdbc/query {:connection con}
|
||||
|
|
@ -79,10 +85,12 @@
|
|||
(execute! con ["select * from fruit"]))
|
||||
;; this is not quite equivalent
|
||||
(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!)
|
||||
(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
|
||||
(jdbc/query {:connection con} ["select * from fruit"]))
|
||||
|
|
@ -137,16 +145,18 @@
|
|||
(execute-one! con ["select * from fruit where appearance = ?" "red"]))
|
||||
|
||||
;; test assoc works
|
||||
(execute-one! con
|
||||
["select * from fruit where appearance = ?" "red"]
|
||||
#(assoc % :test :value)
|
||||
{})
|
||||
(quick-bench
|
||||
(execute-one! con
|
||||
["select * from fruit where appearance = ?" "red"]
|
||||
#(assoc % :test :value)
|
||||
{}))
|
||||
|
||||
;; test assoc works
|
||||
(execute! con
|
||||
["select * from fruit where appearance = ?" "red"]
|
||||
#(assoc % :test :value)
|
||||
{})
|
||||
(quick-bench
|
||||
(execute! con
|
||||
["select * from fruit where appearance = ?" "red"]
|
||||
#(assoc % :test :value)
|
||||
{}))
|
||||
|
||||
(with-transaction [t con {:rollback-only true}]
|
||||
(insert! t :fruit {:id 5, :name "Pear", :appearance "green", :cost 49, :grade 47})
|
||||
|
|
|
|||
Loading…
Reference in a new issue