Back
metadata (clj)
(source)protocol
(metadata this)
Return the raw `ResultSetMetaData` object from the result set.
Should not cause any row realization.
If `next.jdbc.datafy` has been required, this metadata 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.
Examples
next-jdbc
What's left to be tested:
* ReadableColumn protocol extension point"
(:require [clojure.core.protocols :as core-p]
[clojure.datafy :as d]
[clojure.string :as str]
[clojure.test :refer [deftest is testing use-fixtures]]
[next.jdbc.protocols :as p]
[next.jdbc.result-set :as rs]
[next.jdbc.specs :as specs]
[next.jdbc.test-fixtures :refer [with-test-db ds column
default-options
derby? mssql? mysql? postgres?]])
(:import (java.sql ResultSet ResultSetMetaData)))
(deftest test-mapify
(testing "no row builder is used"
(is (= [true]
(into [] (map map?) ; it looks like a real map now
(p/-execute (ds) ["select * from fruit where id = ?" 1]
{:builder-fn (constantly nil)}))))
(is (= ["Apple"]
(into [] (map :name) ; keyword selection works
(p/-execute (ds) ["select * from fruit where id = ?" 1]
{:builder-fn (constantly nil)}))))
(is (= [[2 [:name "Banana"]]]
(into [] (map (juxt #(get % "id") ; get by string key works
#(find % :name))) ; get MapEntry works
(p/-execute (ds) ["select * from fruit where id = ?" 2]
{:builder-fn (constantly nil)}))))
(is (= [{:id 3 :name "Peach"}]
(into [] (map #(select-keys % [:id :name])) ; select-keys works
(p/-execute (ds) ["select * from fruit where id = ?" 3]
{:builder-fn (constantly nil)}))))
(is (= [[:orange 4]]
(into [] (map #(vector (if (contains? % :name) ; contains works
(keyword (str/lower-case (:name %)))
:unnamed)
(get % :id 0))) ; get with not-found works
(p/-execute (ds) ["select * from fruit where id = ?" 4]
{:builder-fn (constantly nil)}))))
(is (= [{}]
(into [] (map empty) ; return empty map without building
(p/-execute (ds) ["select * from fruit where id = ?" 1]
{:builder-fn (constantly nil)})))))
(testing "count does not build a map"
(let [count-builder (fn [_1 _2]
(reify rs/RowBuilder
(column-count [_] 13)))]
(is (= [13]
(into [] (map count) ; count relies on columns, not row fields
(p/-execute (ds) ["select * from fruit where id = ?" 1]
{:builder-fn count-builder}))))))
(testing "assoc, dissoc, cons, seq, and = build maps"
(is (map? (reduce (fn [_ row] (reduced (assoc row :x 1)))
nil
(p/-execute (ds) ["select * from fruit"] {}))))
(is (= 6 (count (reduce (fn [_ row] (reduced (assoc row :x 1)))
nil
(p/-execute (ds) ["select * from fruit"] {})))))
(is (map? (reduce (fn [_ row] (reduced
(dissoc row (column :FRUIT/NAME))))
nil
(p/-execute (ds) ["select * from fruit"]
(default-options)))))
(is (= 4 (count (reduce (fn [_ row] (reduced
(dissoc row (column :FRUIT/NAME))))
nil
(p/-execute (ds) ["select * from fruit"]
(default-options))))))
(is (seq? (reduce (fn [_ row] (reduced (seq row)))
nil
(p/-execute (ds) ["select * from fruit"] {}))))
(is (seq? (reduce (fn [_ row] (reduced (cons :seq row)))
nil
(p/-execute (ds) ["select * from fruit"] {}))))
(is (= :seq (first (reduce (fn [_ row] (reduced (cons :seq row)))
nil
(p/-execute (ds) ["select * from fruit"] {})))))
(is (false? (reduce (fn [_ row] (reduced (= row {})))
nil
(p/-execute (ds) ["select * from fruit"] {}))))
(is (map-entry? (second (reduce (fn [_ row] (reduced (cons :seq row)))
nil
(p/-execute (ds) ["select * from fruit"] {})))))
(is (every? map-entry? (reduce (fn [_ row] (reduced (seq row)))
nil
(p/-execute (ds) ["select * from fruit"] {}))))
(is (map? (reduce (fn [_ row] (reduced (conj row {:a 1})))
nil
(p/-execute (ds) ["select * from fruit"] {}))))
(is (map? (reduce (fn [_ row] (reduced (conj row [:a 1])))
nil
(p/-execute (ds) ["select * from fruit"] {}))))
(is (map? (reduce (fn [_ row] (reduced (conj row {:a 1 :b 2})))
nil
(p/-execute (ds) ["select * from fruit"] {}))))
(is (= 1 (:a (reduce (fn [_ row] (reduced (conj row {:a 1})))
nil
(p/-execute (ds) ["select * from fruit"] {})))))
(is (= 1 (:a (reduce (fn [_ row] (reduced (conj row [:a 1])))
nil
(p/-execute (ds) ["select * from fruit"] {})))))
(is (= 1 (:a (reduce (fn [_ row] (reduced (conj row {:a 1 :b 2})))
nil
(p/-execute (ds) ["select * from fruit"] {})))))
(is (= 2 (:b (reduce (fn [_ row] (reduced (conj row {:a 1 :b 2})))
nil
(p/-execute (ds) ["select * from fruit"] {})))))
(is (vector? (reduce (fn [_ row] (reduced (conj row :a)))
nil
(p/-execute (ds) ["select * from fruit"]
{:builder-fn rs/as-arrays}))))
(is (= :a (peek (reduce (fn [_ row] (reduced (conj row :a)))
nil
(p/-execute (ds) ["select * from fruit"]
{:builder-fn rs/as-arrays})))))
(is (= :b (peek (reduce (fn [_ row] (reduced (conj row :a :b)))
nil
(p/-execute (ds) ["select * from fruit"]
{:builder-fn rs/as-arrays}))))))
(testing "datafiable-row builds map; with metadata"
(is (map? (reduce (fn [_ row] (reduced (rs/datafiable-row row (ds) {})))
nil
(p/-execute (ds) ["select * from fruit"] {}))))
(is (contains? (meta (reduce (fn [_ row] (reduced (rs/datafiable-row row (ds) {})))
nil
(p/-execute (ds) ["select * from fruit"] {})))
`core-p/datafy))))
(deftest metadata-result-set
(let [metadata (with-open [con (p/get-connection (ds) {})]
(-> (.getMetaData con)
(.getTables nil nil nil (into-array ["TABLE" "VIEW"]))
(rs/datafiable-result-set (ds) {})))]
(is (vector? metadata))
(is (map? (first metadata)))
;; we should find :something/table_name with a value of "fruit"
;; may be upper/lower-case, could have any qualifier
(is (some (fn [row]
(some #(and (= "table_name" (-> % key name str/lower-case))
(= "fruit" (-> % val name str/lower-case)))
row))
metadata))))
next-jdbc
(ns next.jdbc.datafy-test
"Tests for the datafy extensions over JDBC types."
(:require [clojure.datafy :as d]
[clojure.set :as set]
[clojure.test :refer [deftest is testing use-fixtures]]
[next.jdbc :as jdbc]
[next.jdbc.datafy]
[next.jdbc.result-set :as rs]
[next.jdbc.specs :as specs]
[next.jdbc.test-fixtures
:refer [with-test-db db ds
derby? jtds? mysql? postgres? sqlite?]]))
(def ^:private basic-database-metadata-keys
"Generic JDBC Connection fields."
#{:JDBCMajorVersion :JDBCMinorVersion :SQLKeywords :SQLStateType :URL
:catalogSeparator :catalogTerm :catalogs
:clientInfoProperties :connection
:databaseMajorVersion :databaseMinorVersion
:databaseProductName :databaseProductVersion
:defaultTransactionIsolation
:driverMajorVersion :driverMinorVersion :driverName :driverVersion
:extraNameCharacters :identifierQuoteString
:maxBinaryLiteralLength :maxCatalogNameLength :maxCharLiteralLength
:maxColumnNameLength :maxColumnsInGroupBy :maxColumnsInIndex
:maxColumnsInOrderBy :maxColumnsInSelect :maxColumnsInTable
:maxConnections
:maxCursorNameLength :maxIndexLength
:maxProcedureNameLength :maxRowSize :maxSchemaNameLength
:maxStatementLength :maxStatements :maxTableNameLength
:maxTablesInSelect :maxUserNameLength :numericFunctions
:procedureTerm :resultSetHoldability :rowIdLifetime
:schemaTerm :schemas :searchStringEscape :stringFunctions
:systemFunctions :tableTypes :timeDateFunctions
:typeInfo :userName
;; boolean properties
:catalogAtStart :readOnly
;; 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))]
(let [reference-keys (cond-> basic-database-metadata-keys
(jtds?) (-> (disj :clientInfoProperties :rowIdLifetime)
(conj :clientInfoProperties/exception
:rowIdLifetime/exception))
(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 (format "%6s :%-10s %s"
(:dbtype (db)) "db-meta" (str (sort diff)))))
(is (= reference-keys
(set/intersection reference-keys data))))))
(testing "nav to catalogs yields object"
(with-open [con (jdbc/get-connection (ds))]
(let [data (d/datafy (.getMetaData con))]
(doseq [k (cond-> #{:catalogs :clientInfoProperties :schemas :tableTypes :typeInfo}
(jtds?) (disj :clientInfoProperties)
(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"
(let [data (reduce (fn [_ row] (reduced (rs/metadata row)))
nil
(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)))))