Extend datafication and simplify

Handles weird SQLite edge case; datafies Statement and ResultSet; 
improves exception handling in `safe-bean` (but we really need a better 
bean maker).
This commit is contained in:
Sean Corfield 2020-05-31 03:02:25 -07:00
parent e406c90d6f
commit 4be2b7130b
2 changed files with 46 additions and 22 deletions

View file

@ -12,7 +12,8 @@
[next.jdbc.result-set :as rs])
(:import (java.sql Connection
DatabaseMetaData
ResultSetMetaData)))
ResultSet ResultSetMetaData
Statement)))
(set! *warn-on-reflection* true)
@ -46,22 +47,34 @@
;; ensure we return a basic hash map:
(merge {} (bean o))
(catch Throwable t
{:exception (ex-message t)
:cause (ex-message (ex-cause t))})))
(let [dex (juxt type (comp str ex-message))
cause (ex-cause t)]
(with-meta (cond-> {:exception (dex t)}
cause (assoc :cause (dex cause)))
{:exception t})))))
(defn- datafy-result-set-meta-data
[^ResultSetMetaData this]
(mapv #(reduce-kv (fn [m k f] (assoc m k (f this %)))
{}
column-meta)
(range 1 (inc (.getColumnCount this)))))
(extend-protocol core-p/Datafiable
Connection
(datafy [this]
(with-meta (safe-bean this)
{`core-p/nav (fn [_ k v]
(if (= :metaData k)
(.getMetaData this)
v))}))
(datafy [this] (safe-bean this))
DatabaseMetaData
(datafy [this]
(with-meta (safe-bean this)
(with-meta (let [data (safe-bean this)]
(cond-> data
(not (:exception (meta data)))
(assoc :all-tables [])))
{`core-p/nav (fn [_ k v]
(condp = k
:all-tables
(rs/datafiable-result-set (.getTables this nil nil nil nil)
(.getConnection this)
{})
:catalogs
(rs/datafiable-result-set (.getCatalogs this)
(.getConnection this)
@ -84,8 +97,15 @@
{})
v))}))
ResultSetMetaData
(datafy [this] (datafy-result-set-meta-data this))
ResultSet
(datafy [this]
(mapv #(reduce-kv (fn [m k f] (assoc m k (f this %)))
{}
column-meta)
(range 1 (inc (.getColumnCount this))))))
;; SQLite has a combination ResultSet/Metadata object...
(if (instance? ResultSetMetaData this)
(datafy-result-set-meta-data this)
(let [s (.getStatement this)
c (when s (.getConnection s))]
(cond-> (safe-bean this)
c (assoc :rows (rs/datafiable-result-set this c {}))))))
Statement
(datafy [this] (safe-bean this)))

View file

@ -10,7 +10,7 @@
[next.jdbc.result-set :as rs]
[next.jdbc.specs :as specs]
[next.jdbc.test-fixtures :refer [with-test-db db ds
derby? postgres? sqlite?]])
derby? mysql? postgres? sqlite?]])
(:import (java.sql DatabaseMetaData)))
(set! *warn-on-reflection* true)
@ -38,12 +38,7 @@
(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)))))))
(testing "nav to metadata yields object"
(when-not (derby?)
(with-open [con (jdbc/get-connection (ds))]
(is (instance? DatabaseMetaData
(d/nav (d/datafy con) :metaData nil)))))))
(set/intersection basic-connection-keys data))))))))
(def ^:private basic-database-metadata-keys
"Generic JDBC Connection fields."
@ -96,8 +91,17 @@
(testing "result set metadata datafication"
(let [data (reduce (fn [_ row] (reduced (rs/metadata row)))
nil
(jdbc/plan (ds) ["select * from fruit"]))]
(jdbc/plan (ds) [(str "SELECT * FROM "
(if (mysql?) "fruit" "FRUIT"))]))]
(is (vector? data))
(is (= 5 (count data)))
(is (every? map? data))
(is (every? :label data)))))
(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"]))
(.execute ps)
(.getResultSet ps)
(.close con))