From 4be2b7130bfc832afdba0f37f725d316ece9101c Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Sun, 31 May 2020 03:02:25 -0700 Subject: [PATCH] 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). --- src/next/jdbc/datafy.clj | 48 ++++++++++++++++++++++++---------- test/next/jdbc/datafy_test.clj | 20 ++++++++------ 2 files changed, 46 insertions(+), 22 deletions(-) diff --git a/src/next/jdbc/datafy.clj b/src/next/jdbc/datafy.clj index 2bee2e2..7489627 100644 --- a/src/next/jdbc/datafy.clj +++ b/src/next/jdbc/datafy.clj @@ -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))) diff --git a/test/next/jdbc/datafy_test.clj b/test/next/jdbc/datafy_test.clj index 736eed5..c39d860 100644 --- a/test/next/jdbc/datafy_test.clj +++ b/test/next/jdbc/datafy_test.clj @@ -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))