Merge pull request #113 from seancorfield/java.data-next
Expand datafication; leverage new exception handling in java.data
This commit is contained in:
commit
d24dd892dd
3 changed files with 71 additions and 36 deletions
2
deps.edn
2
deps.edn
|
|
@ -1,6 +1,6 @@
|
|||
{:paths ["src"]
|
||||
:deps {org.clojure/clojure {:mvn/version "1.10.1"}
|
||||
org.clojure/java.data {:mvn/version "1.0.73"}}
|
||||
org.clojure/java.data {:mvn/version "1.0.78"}}
|
||||
:aliases
|
||||
{:test {:extra-paths ["test"]
|
||||
:extra-deps {org.clojure/test.check {:mvn/version "1.0.0"}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,33 @@
|
|||
;; copyright (c) 2020 Sean Corfield, all rights reserved
|
||||
|
||||
(ns next.jdbc.datafy
|
||||
"This namespace provides datafication of several JDBC object types:
|
||||
"This namespace provides datafication of several JDBC object types,
|
||||
all within the `java.sql` package:
|
||||
|
||||
* `java.sql.Connection` -- datafies as a bean; `:metaData` is navigable
|
||||
* `Connection` -- datafies as a bean; `:metaData` is navigable
|
||||
and produces `java.sql.DatabaseMetaData`.
|
||||
* `java.sql.DatabaseMetaData` -- datafies as a bean; five properties
|
||||
* `DatabaseMetaData` -- datafies as a bean; five properties
|
||||
are navigable to produce fully-realized datafiable result sets.
|
||||
* `java.sql.ResultSetMetaData` -- datafies as a vector of column descriptions."
|
||||
* `ParameterMetaData` -- datafies as a vector of parameter descriptions.
|
||||
* `ResultSet` -- datafies as a bean; if the `ResultSet` has an associated
|
||||
`Statement` and that in turn has an associated `Connection` then an
|
||||
additional key of `:rows` is provided which is a datafied result set,
|
||||
from `next.jdbc.result-set/datafiable-result-set` with default options.
|
||||
This is provided as a convenience, purely for datafication of other
|
||||
JDBC data types -- in normal `next.jdbc` usage, result sets are
|
||||
datafied under full user control.
|
||||
* `ResultSetMetaData` -- datafies as a vector of column descriptions.
|
||||
* `Statement` -- datafies as a bean.
|
||||
|
||||
Because different database drivers may throw `SQLException` for various
|
||||
unimplemented or unavailable propertiies on objects in various states,
|
||||
the default behavior is to return those exceptions using the `:qualify`
|
||||
option for `clojure.java.data/from-java-shallow`, so for a property
|
||||
`:foo`, if its corresponding getter throws an exception, it would instead
|
||||
be returned as `:foo/exception`. This behavior can be overridden by
|
||||
`binding` `next.jdbc.datafy/*datafy-failure*` to any of the other options
|
||||
supported: `:group`, `:omit`, or `:return`. See the `clojure.java.data`
|
||||
documentation for more details."
|
||||
(:require [clojure.core.protocols :as core-p]
|
||||
[clojure.java.data :as j]
|
||||
[next.jdbc.result-set :as rs])
|
||||
|
|
@ -64,9 +84,15 @@
|
|||
:unknown))
|
||||
:signed (fn [^ParameterMetaData o i] (.isSigned o i))})
|
||||
|
||||
(def ^:dynamic *datafy-failure*
|
||||
"How datafication failures should be handled, based on `clojure.java.data`.
|
||||
|
||||
Defaults to `:qualify`, but can be `:group`, `:omit`, `:qualify`, or `:return`."
|
||||
:qualify)
|
||||
|
||||
(defn- safe-bean [o opts]
|
||||
(try
|
||||
(j/from-java-shallow o (assoc opts :add-class true))
|
||||
(j/from-java-shallow o (assoc opts :add-class true :exceptions *datafy-failure*))
|
||||
(catch Throwable t
|
||||
(let [dex (juxt type (comp str ex-message))
|
||||
cause (ex-cause t)]
|
||||
|
|
@ -96,7 +122,8 @@
|
|||
(with-meta (let [data (safe-bean this {})]
|
||||
(cond-> data
|
||||
(not (:exception (meta data)))
|
||||
(assoc :all-tables [])))
|
||||
;; add an opaque object that nav will "replace"
|
||||
(assoc :all-tables (Object.))))
|
||||
{`core-p/nav (fn [_ k v]
|
||||
(condp = k
|
||||
:all-tables
|
||||
|
|
@ -124,8 +151,6 @@
|
|||
(.getConnection this)
|
||||
{})
|
||||
v))}))
|
||||
ResultSetMetaData
|
||||
(datafy [this] (datafy-result-set-meta-data this))
|
||||
ParameterMetaData
|
||||
(datafy [this] (datafy-parameter-meta-data this))
|
||||
ResultSet
|
||||
|
|
@ -137,5 +162,7 @@
|
|||
c (when s (.getConnection s))]
|
||||
(cond-> (safe-bean this {})
|
||||
c (assoc :rows (rs/datafiable-result-set this c {}))))))
|
||||
ResultSetMetaData
|
||||
(datafy [this] (datafy-result-set-meta-data this))
|
||||
Statement
|
||||
(datafy [this] (safe-bean this {:omit #{:moreResults}})))
|
||||
|
|
|
|||
|
|
@ -24,20 +24,20 @@
|
|||
:networkTimeout :schema :transactionIsolation :typeMap :warnings
|
||||
;; boolean properties
|
||||
:closed :readOnly
|
||||
;; added by bean itself
|
||||
;; configured to be added as if by clojure.core/bean
|
||||
:class})
|
||||
|
||||
(deftest connection-datafy-tests
|
||||
(testing "connection datafication"
|
||||
(with-open [con (jdbc/get-connection (ds))]
|
||||
(if (derby?)
|
||||
(is (= #{:exception :cause} ; at least one property not supported
|
||||
(set (keys (d/datafy con)))))
|
||||
(let [data (set (keys (d/datafy con)))]
|
||||
(when-let [diff (seq (set/difference data basic-connection-keys))]
|
||||
(println (:dbtype (db)) :connection (sort diff)))
|
||||
(is (= basic-connection-keys
|
||||
(set/intersection basic-connection-keys data))))))))
|
||||
(let [reference-keys (cond-> basic-connection-keys
|
||||
(derby?) (-> (disj :networkTimeout)
|
||||
(conj :networkTimeout/exception)))
|
||||
data (set (keys (d/datafy con)))]
|
||||
(when-let [diff (seq (set/difference data reference-keys))]
|
||||
(println (:dbtype (db)) :connection (sort diff)))
|
||||
(is (= reference-keys
|
||||
(set/intersection reference-keys data)))))))
|
||||
|
||||
(def ^:private basic-database-metadata-keys
|
||||
"Generic JDBC Connection fields."
|
||||
|
|
@ -63,28 +63,33 @@
|
|||
:typeInfo :userName
|
||||
;; boolean properties
|
||||
:catalogAtStart :readOnly
|
||||
;; added by bean itself
|
||||
:class})
|
||||
;; configured to be added as if by clojure.core/bean
|
||||
:class
|
||||
;; added by next.jdbc.datafy if the datafication succeeds
|
||||
:all-tables})
|
||||
|
||||
(deftest database-metadata-datafy-tests
|
||||
(testing "database metadata datafication"
|
||||
(with-open [con (jdbc/get-connection (ds))]
|
||||
(if (or (postgres?) (sqlite?))
|
||||
(is (= #{:exception :cause} ; at least one property not supported
|
||||
(set (keys (d/datafy (.getMetaData con))))))
|
||||
(let [data (set (keys (d/datafy (.getMetaData con))))]
|
||||
(when-let [diff (seq (set/difference data basic-database-metadata-keys))]
|
||||
(println (:dbtype (db)) :db-meta (sort diff)))
|
||||
(is (= basic-database-metadata-keys
|
||||
(set/intersection basic-database-metadata-keys data)))))))
|
||||
(let [reference-keys (cond-> basic-database-metadata-keys
|
||||
(postgres?) (-> (disj :rowIdLifetime)
|
||||
(conj :rowIdLifetime/exception))
|
||||
(sqlite?) (-> (disj :clientInfoProperties :rowIdLifetime)
|
||||
(conj :clientInfoProperties/exception
|
||||
:rowIdLifetime/exception)))
|
||||
data (set (keys (d/datafy (.getMetaData con))))]
|
||||
(when-let [diff (seq (set/difference data reference-keys))]
|
||||
(println (:dbtype (db)) :db-meta (sort diff)))
|
||||
(is (= reference-keys
|
||||
(set/intersection reference-keys data))))))
|
||||
(testing "nav to catalogs yields object"
|
||||
(when-not (or (postgres?) (sqlite?))
|
||||
(with-open [con (jdbc/get-connection (ds))]
|
||||
(let [data (d/datafy (.getMetaData con))]
|
||||
(doseq [k [:catalogs :clientInfoProperties :schemas :tableTypes :typeInfo]]
|
||||
(let [rs (d/nav data k nil)]
|
||||
(is (vector? rs))
|
||||
(is (every? map? rs)))))))))
|
||||
(with-open [con (jdbc/get-connection (ds))]
|
||||
(let [data (d/datafy (.getMetaData con))]
|
||||
(doseq [k (cond-> #{:catalogs :clientInfoProperties :schemas :tableTypes :typeInfo}
|
||||
(sqlite?) (disj :clientInfoProperties))]
|
||||
(let [rs (d/nav data k nil)]
|
||||
(is (vector? rs))
|
||||
(is (every? map? rs))))))))
|
||||
|
||||
(deftest result-set-metadata-datafy-tests
|
||||
(testing "result set metadata datafication"
|
||||
|
|
@ -100,7 +105,10 @@
|
|||
(comment
|
||||
(def con (jdbc/get-connection (ds)))
|
||||
(rs/datafiable-result-set (.getTables (.getMetaData con) nil nil nil nil) con {})
|
||||
(def ps (jdbc/prepare con ["SELECT * FROM fruit"]))
|
||||
(def ps (jdbc/prepare con ["SELECT * FROM fruit WHERE grade > ?"]))
|
||||
(require '[next.jdbc.prepare :as prep])
|
||||
(prep/set-parameters ps [30])
|
||||
(.execute ps)
|
||||
(.getResultSet ps)
|
||||
(.close ps)
|
||||
(.close con))
|
||||
|
|
|
|||
Loading…
Reference in a new issue