Fix #6 by adding IReadColumn and ISQLParameter protocols
The latter can be extended via metadata but the former cannot (since only the latter is coming from Clojure).
This commit is contained in:
parent
4b81a42b4d
commit
6d1a42a0a0
2 changed files with 61 additions and 12 deletions
|
|
@ -14,6 +14,24 @@
|
||||||
|
|
||||||
(set! *warn-on-reflection* true)
|
(set! *warn-on-reflection* true)
|
||||||
|
|
||||||
|
(defprotocol ISQLParameter :extend-via-metadata true
|
||||||
|
"Protocol for setting SQL parameters in statement objects, which
|
||||||
|
can convert from Clojure values. The default implementation just
|
||||||
|
calls .setObject on the parameter value. It can be extended to use other
|
||||||
|
methods of PreparedStatement to convert and set parameter values."
|
||||||
|
(set-parameter [val stmt ix]
|
||||||
|
"Convert a Clojure value into a SQL value and store it as the ix'th
|
||||||
|
parameter in the given SQL statement object."))
|
||||||
|
|
||||||
|
(extend-protocol ISQLParameter
|
||||||
|
Object
|
||||||
|
(set-parameter [v ^PreparedStatement s ^long i]
|
||||||
|
(.setObject s i v))
|
||||||
|
|
||||||
|
nil
|
||||||
|
(set-parameter [_ ^PreparedStatement s ^long i]
|
||||||
|
(.setObject s i nil)))
|
||||||
|
|
||||||
(defn set-parameters
|
(defn set-parameters
|
||||||
"Given a PreparedStatement and a vector of parameter values, update the
|
"Given a PreparedStatement and a vector of parameter values, update the
|
||||||
PreparedStatement with those parameters and return it.
|
PreparedStatement with those parameters and return it.
|
||||||
|
|
@ -23,7 +41,7 @@
|
||||||
[^PreparedStatement ps params]
|
[^PreparedStatement ps params]
|
||||||
(when (seq params)
|
(when (seq params)
|
||||||
(loop [[p & more] params i 1]
|
(loop [[p & more] params i 1]
|
||||||
(.setObject ps i p)
|
(set-parameter p ps i)
|
||||||
(when more
|
(when more
|
||||||
(recur more (inc i)))))
|
(recur more (inc i)))))
|
||||||
ps)
|
ps)
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,8 @@
|
||||||
|
|
||||||
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 opts]
|
[^ResultSet rs ^ResultSetMetaData rsmeta opts]
|
||||||
(let [^ResultSetMetaData rsmeta (.getMetaData rs)
|
(let [idxs (range 1 (inc (.getColumnCount rsmeta)))]
|
||||||
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]
|
||||||
(keyword (when-let [qualifier (not-empty (.getTableName rsmeta i))]
|
(keyword (when-let [qualifier (not-empty (.getTableName rsmeta i))]
|
||||||
|
|
@ -32,8 +31,33 @@
|
||||||
idxs))))
|
idxs))))
|
||||||
|
|
||||||
(defprotocol ColumnarResultSet
|
(defprotocol ColumnarResultSet
|
||||||
(column-names [this])
|
"To allow reducing functions to access a result set's column names and
|
||||||
(row-values [this]))
|
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 IReadColumn
|
||||||
|
"Protocol for reading objects from the java.sql.ResultSet. Default
|
||||||
|
implementations (for Object and nil) return the argument, and the
|
||||||
|
Boolean implementation ensures a canonicalized true/false value,
|
||||||
|
but it can be extended to provide custom behavior for special types."
|
||||||
|
(read-column-by-label [val label]
|
||||||
|
"Function for transforming values after reading them via a column label.")
|
||||||
|
(read-column-by-index [val rsmeta idx]
|
||||||
|
"Function for transforming values after reading them via a column index."))
|
||||||
|
|
||||||
|
(extend-protocol IReadColumn
|
||||||
|
Object
|
||||||
|
(read-column-by-label [x _] x)
|
||||||
|
(read-column-by-index [x _2 _3] x)
|
||||||
|
|
||||||
|
Boolean
|
||||||
|
(read-column-by-label [x _] (if (= true x) true false))
|
||||||
|
(read-column-by-index [x _2 _3] (if (= true x) true false))
|
||||||
|
|
||||||
|
nil
|
||||||
|
(read-column-by-label [_1 _2] nil)
|
||||||
|
(read-column-by-index [_1 _2 _3] nil))
|
||||||
|
|
||||||
(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
|
||||||
|
|
@ -47,17 +71,18 @@
|
||||||
|
|
||||||
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 [cols (delay (get-column-names rs opts))]
|
(let [rsmeta (.getMetaData rs)
|
||||||
|
cols (delay (get-column-names rs rsmeta opts))]
|
||||||
(reify
|
(reify
|
||||||
|
|
||||||
clojure.lang.ILookup
|
clojure.lang.ILookup
|
||||||
(valAt [this k]
|
(valAt [this k]
|
||||||
(try
|
(try
|
||||||
(.getObject rs (name k))
|
(read-column-by-label (.getObject rs (name k)) (name k))
|
||||||
(catch SQLException _)))
|
(catch SQLException _)))
|
||||||
(valAt [this k not-found]
|
(valAt [this k not-found]
|
||||||
(try
|
(try
|
||||||
(.getObject rs (name k))
|
(read-column-by-label (.getObject rs (name k)) (name k))
|
||||||
(catch SQLException _
|
(catch SQLException _
|
||||||
not-found)))
|
not-found)))
|
||||||
|
|
||||||
|
|
@ -70,7 +95,9 @@
|
||||||
false)))
|
false)))
|
||||||
(entryAt [this k]
|
(entryAt [this k]
|
||||||
(try
|
(try
|
||||||
(clojure.lang.MapEntry. k (.getObject rs (name k)))
|
(clojure.lang.MapEntry. k (read-column-by-label
|
||||||
|
(.getObject rs (name k))
|
||||||
|
(name k)))
|
||||||
(catch SQLException _)))
|
(catch SQLException _)))
|
||||||
(assoc [this k v]
|
(assoc [this k v]
|
||||||
(assoc (into {} (seq this)) k v))
|
(assoc (into {} (seq this)) k v))
|
||||||
|
|
@ -79,13 +106,17 @@
|
||||||
(seq [this]
|
(seq [this]
|
||||||
(seq (mapv (fn [^Integer i]
|
(seq (mapv (fn [^Integer i]
|
||||||
(clojure.lang.MapEntry. (nth @cols (dec i))
|
(clojure.lang.MapEntry. (nth @cols (dec i))
|
||||||
(.getObject rs i)))
|
(read-column-by-index
|
||||||
|
(.getObject rs i)
|
||||||
|
rsmeta i)))
|
||||||
(range 1 (inc (count @cols))))))
|
(range 1 (inc (count @cols))))))
|
||||||
|
|
||||||
ColumnarResultSet
|
ColumnarResultSet
|
||||||
(column-names [this] @cols)
|
(column-names [this] @cols)
|
||||||
(row-values [this]
|
(row-values [this]
|
||||||
(mapv (fn [^Integer i] (.getObject rs i))
|
(mapv (fn [^Integer i] (read-column-by-index
|
||||||
|
(.getObject rs i)
|
||||||
|
rsmeta i))
|
||||||
(range 1 (inc (count @cols))))))))
|
(range 1 (inc (count @cols))))))))
|
||||||
|
|
||||||
(defn as-arrays
|
(defn as-arrays
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue