Expand datafy/nav and metadata support (work in progress)
This commit is contained in:
parent
8f6844aa5d
commit
6a6e42e9af
4 changed files with 156 additions and 3 deletions
|
|
@ -2,6 +2,10 @@
|
|||
|
||||
Only accretive/fixative changes will be made from now on.
|
||||
|
||||
Changes made since release 1.0.445:
|
||||
* Addition of `next.jdbc.datafy` to provide more `datafy`/`nav` introspection (work in progress; documentation pending).
|
||||
* Addition of `next.jdbc.result-set/metadata` to provide (datafied) result set metadata within `plan`.
|
||||
|
||||
## Stable Builds
|
||||
|
||||
* 2020-05-23 -- 1.0.445
|
||||
|
|
|
|||
91
src/next/jdbc/datafy.clj
Normal file
91
src/next/jdbc/datafy.clj
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
;; copyright (c) 2018-2020 Sean Corfield, all rights reserved
|
||||
|
||||
(ns next.jdbc.datafy
|
||||
"This namespace provides datafication of several JDBC object types:
|
||||
|
||||
* `java.sql.Connection` -- datafies as a bean; `:metaData` is navigable
|
||||
and produces `java.sql.DatabaseMetaData`.
|
||||
* `java.sql.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."
|
||||
(:require [clojure.core.protocols :as core-p]
|
||||
[next.jdbc.result-set :as rs])
|
||||
(:import (java.sql Connection
|
||||
DatabaseMetaData
|
||||
ResultSetMetaData)))
|
||||
|
||||
(set! *warn-on-reflection* true)
|
||||
|
||||
(def ^:private column-meta
|
||||
{:catalog (fn [^ResultSetMetaData o i] (.getCatalogName o i))
|
||||
:class (fn [^ResultSetMetaData o i] (.getColumnClassName o i))
|
||||
:display-size (fn [^ResultSetMetaData o i] (.getColumnDisplaySize o i))
|
||||
:label (fn [^ResultSetMetaData o i] (.getColumnLabel o i))
|
||||
:name (fn [^ResultSetMetaData o i] (.getColumnName o i))
|
||||
:precision (fn [^ResultSetMetaData o i] (.getPrecision o i))
|
||||
:scale (fn [^ResultSetMetaData o i] (.getScale o i))
|
||||
:schema (fn [^ResultSetMetaData o i] (.getSchemaName o i))
|
||||
:table (fn [^ResultSetMetaData o i] (.getTableName o i))
|
||||
;; the is* fields:
|
||||
:nullability (fn [^ResultSetMetaData o i]
|
||||
(condp = (.isNullable o i)
|
||||
ResultSetMetaData/columnNoNulls :not-null
|
||||
ResultSetMetaData/columnNullable :null
|
||||
:unknown))
|
||||
:auto-increment (fn [^ResultSetMetaData o i] (.isAutoIncrement o i))
|
||||
:case-sensitive (fn [^ResultSetMetaData o i] (.isCaseSensitive o i))
|
||||
:currency (fn [^ResultSetMetaData o i] (.isCurrency o i))
|
||||
:definitely-writable (fn [^ResultSetMetaData o i] (.isDefinitelyWritable o i))
|
||||
:read-only (fn [^ResultSetMetaData o i] (.isReadOnly o i))
|
||||
:searchable (fn [^ResultSetMetaData o i] (.isSearchable o i))
|
||||
:signed (fn [^ResultSetMetaData o i] (.isSigned o i))
|
||||
:writable (fn [^ResultSetMetaData o i] (.isWritable o i))})
|
||||
|
||||
(defn- safe-bean [o]
|
||||
(try
|
||||
;; ensure we return a basic hash map:
|
||||
(merge {} (bean o))
|
||||
(catch Throwable t
|
||||
{:exception (ex-message t)
|
||||
:cause (ex-message (ex-cause t))})))
|
||||
|
||||
(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))}))
|
||||
DatabaseMetaData
|
||||
(datafy [this]
|
||||
(with-meta (safe-bean this)
|
||||
{`core-p/nav (fn [_ k v]
|
||||
(condp = k
|
||||
:catalogs
|
||||
(rs/datafiable-result-set (.getCatalogs this)
|
||||
(.getConnection this)
|
||||
{})
|
||||
:clientInfoProperties
|
||||
(rs/datafiable-result-set (.getClientInfoProperties this)
|
||||
(.getConnection this)
|
||||
{})
|
||||
:schemas
|
||||
(rs/datafiable-result-set (.getSchemas this)
|
||||
(.getConnection this)
|
||||
{})
|
||||
:tableTypes
|
||||
(rs/datafiable-result-set (.getTableTypes this)
|
||||
(.getConnection this)
|
||||
{})
|
||||
:typeInfo
|
||||
(rs/datafiable-result-set (.getTypeInfo this)
|
||||
(.getConnection this)
|
||||
{})
|
||||
v))}))
|
||||
ResultSetMetaData
|
||||
(datafy [this]
|
||||
(mapv #(reduce-kv (fn [m k f] (assoc m k (f this %)))
|
||||
{}
|
||||
column-meta)
|
||||
(range 1 (inc (.getColumnCount this))))))
|
||||
|
|
@ -397,7 +397,14 @@
|
|||
(row-number [this]
|
||||
"Return the current 1-based row number, if available.")
|
||||
(column-names [this]
|
||||
"Return a vector of the column names from the result set."))
|
||||
"Return a vector of the column names from the result set.")
|
||||
(metadata [this]
|
||||
"Return the raw `ResultSetMetaData` object from the result set.
|
||||
|
||||
If `next.jdbc.datafy` has been required, this will be fully-realized
|
||||
as a Clojure data structure, otherwise this should not be allowed to
|
||||
'leak' outside of the reducing function as it may depend on the
|
||||
connection remaining open, in order to be valid."))
|
||||
|
||||
(defn- mapify-result-set
|
||||
"Given a `ResultSet`, return an object that wraps the current row as a hash
|
||||
|
|
@ -420,6 +427,7 @@
|
|||
InspectableMapifiedResultSet
|
||||
(row-number [this] (.getRow rs))
|
||||
(column-names [this] (:cols @builder))
|
||||
(metadata [this] (core-p/datafy (:rsmeta @builder)))
|
||||
|
||||
clojure.lang.IPersistentMap
|
||||
(assoc [this k v]
|
||||
|
|
@ -500,7 +508,8 @@
|
|||
;; since we have to call these eagerly, we trap any exceptions so
|
||||
;; that they can be thrown when the actual functions are called
|
||||
(let [row (try (.getRow rs) (catch Throwable t t))
|
||||
cols (try (:cols @builder) (catch Throwable t t))]
|
||||
cols (try (:cols @builder) (catch Throwable t t))
|
||||
meta (try (core-p/datafy (:rsmeta @builder)) (catch Throwable t t))]
|
||||
(with-meta
|
||||
(row-builder @builder)
|
||||
{`core-p/datafy
|
||||
|
|
@ -508,7 +517,9 @@
|
|||
`row-number
|
||||
(fn [_] (if (instance? Throwable row) (throw row) row))
|
||||
`column-names
|
||||
(fn [_] (if (instance? Throwable cols) (throw cols) cols))})))
|
||||
(fn [_] (if (instance? Throwable cols) (throw cols) cols))
|
||||
`metadata
|
||||
(fn [_] (if (instance? Throwable meta) (throw meta) meta))})))
|
||||
|
||||
(toString [_]
|
||||
(try
|
||||
|
|
|
|||
47
test/next/jdbc/datafy_test.clj
Normal file
47
test/next/jdbc/datafy_test.clj
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
;; copyright (c) 2019-2020 Sean Corfield, all rights reserved
|
||||
|
||||
(ns next.jdbc.datafy-test
|
||||
"Tests for the datafy extensions over JDBC types."
|
||||
(:require [clojure.core.protocols :as core-p]
|
||||
[clojure.set :as set]
|
||||
[clojure.string :as str]
|
||||
[clojure.test :refer [deftest is testing use-fixtures]]
|
||||
[next.jdbc :as jdbc]
|
||||
[next.jdbc.datafy]
|
||||
[next.jdbc.test-fixtures :refer [with-test-db db ds
|
||||
derby?
|
||||
mssql?]]
|
||||
[next.jdbc.specs :as specs])
|
||||
(:import (java.sql ResultSet)))
|
||||
|
||||
(set! *warn-on-reflection* true)
|
||||
|
||||
(use-fixtures :once with-test-db)
|
||||
|
||||
(specs/instrument)
|
||||
|
||||
(def ^:private basic-connection-keys
|
||||
"Generic JDBC Connection fields."
|
||||
#{:autoCommit :catalog :clientInfo :holdability :metaData
|
||||
:networkTimeout :schema :transactionIsolation :typeMap :warnings
|
||||
;; boolean properties
|
||||
:closed :readOnly
|
||||
;; added by bean itself
|
||||
:class})
|
||||
|
||||
(deftest connection-datafy-tests
|
||||
(testing "basic datafication"
|
||||
(if (derby?)
|
||||
(is (= #{:exception :cause} ; at least one property not supported
|
||||
(set (keys (core-p/datafy (jdbc/get-connection (ds)))))))
|
||||
(let [data (set (keys (core-p/datafy (jdbc/get-connection (ds)))))]
|
||||
(when-let [diff (seq (set/difference data basic-connection-keys))]
|
||||
(println (:dbtype (db)) (sort diff)))
|
||||
(is (= basic-connection-keys
|
||||
(set/intersection basic-connection-keys data))))))
|
||||
(testing "nav to metadata yields object"
|
||||
(when-not (derby?)
|
||||
(is (instance? java.sql.DatabaseMetaData
|
||||
(core-p/nav (core-p/datafy (jdbc/get-connection (ds)))
|
||||
:metaData
|
||||
nil))))))
|
||||
Loading…
Reference in a new issue