Add tests for map-like reducible! result set
Ensures basic associative/lookup access do not cause row building; ensures assoc/seq produce real maps; ensure datafiable-row produces a real map with metadata.
This commit is contained in:
parent
bfda745a70
commit
12e396683c
3 changed files with 63 additions and 43 deletions
|
|
@ -165,14 +165,19 @@
|
||||||
map. Note that a result set is mutable and the current row will change behind
|
map. Note that a result set is mutable and the current row will change behind
|
||||||
this wrapper so operations need to be eager (and fairly limited).
|
this wrapper so operations need to be eager (and fairly limited).
|
||||||
|
|
||||||
|
In particular, this does not satisfy `map?` because it does not implement
|
||||||
|
all of IPersistentMap.
|
||||||
|
|
||||||
Supports ILookup (keywords are treated as strings).
|
Supports ILookup (keywords are treated as strings).
|
||||||
|
|
||||||
Supports Associative (again, keywords are treated as strings). If you assoc,
|
Supports Associative (again, keywords are treated as strings). If you assoc,
|
||||||
a full row will be realized (via `row-builder` above).
|
a full row will be realized (via `row-builder` above).
|
||||||
|
|
||||||
Supports Seqable which realizes a full row of the data."
|
Supports Seqable which realizes a full row of the data.
|
||||||
|
|
||||||
|
Supports DatafiableRow (which realizes a full row of the data)."
|
||||||
[^ResultSet rs opts]
|
[^ResultSet rs opts]
|
||||||
(let [gen (delay ((get :gen-fn opts as-maps) rs opts))]
|
(let [gen (delay ((get opts :gen-fn as-maps) rs opts))]
|
||||||
(reify
|
(reify
|
||||||
|
|
||||||
clojure.lang.ILookup
|
clojure.lang.ILookup
|
||||||
|
|
|
||||||
|
|
@ -4,17 +4,16 @@
|
||||||
"Stub test namespace for the result set functions.
|
"Stub test namespace for the result set functions.
|
||||||
|
|
||||||
There's so much that should be tested here:
|
There's so much that should be tested here:
|
||||||
* column name generation functions
|
|
||||||
* ReadableColumn protocol extension point
|
* ReadableColumn protocol extension point
|
||||||
* RowBuilder and ResultSetBuilder machinery
|
|
||||||
* ResultSet-as-map for reducible! / -execute protocol
|
|
||||||
* -execute-one and -execute-all implementations"
|
* -execute-one and -execute-all implementations"
|
||||||
(:require [clojure.datafy :as d]
|
(:require [clojure.core.protocols :as core-p]
|
||||||
|
[clojure.datafy :as d]
|
||||||
[clojure.string :as str]
|
[clojure.string :as str]
|
||||||
[clojure.test :refer [deftest is testing use-fixtures]]
|
[clojure.test :refer [deftest is testing use-fixtures]]
|
||||||
[next.jdbc.protocols :as p]
|
[next.jdbc.protocols :as p]
|
||||||
[next.jdbc.result-set :as rs]
|
[next.jdbc.result-set :as rs]
|
||||||
[next.jdbc.test-fixtures :refer [with-test-db ds]]))
|
[next.jdbc.test-fixtures :refer [with-test-db ds]])
|
||||||
|
(:import (java.sql ResultSet ResultSetMetaData)))
|
||||||
|
|
||||||
(use-fixtures :once with-test-db)
|
(use-fixtures :once with-test-db)
|
||||||
|
|
||||||
|
|
@ -59,16 +58,14 @@
|
||||||
(is (= 3 (:FRUIT/ID (first object))))
|
(is (= 3 (:FRUIT/ID (first object))))
|
||||||
(is (= "Peach" (:FRUIT/NAME (first object))))))))
|
(is (= "Peach" (:FRUIT/NAME (first object))))))))
|
||||||
|
|
||||||
|
(defn lower-case-cols [^ResultSetMetaData rsmeta opts]
|
||||||
|
(mapv (fn [^Integer i]
|
||||||
|
(keyword (str/lower-case (.getColumnLabel rsmeta i))))
|
||||||
|
(range 1 (inc (.getColumnCount rsmeta)))))
|
||||||
|
|
||||||
(defn get-lower-column-names [^java.sql.ResultSetMetaData rsmeta opts]
|
(defn as-lower-case [^ResultSet rs opts]
|
||||||
(let [idxs (range 1 (inc (.getColumnCount rsmeta)))]
|
|
||||||
(mapv (fn [^Integer i]
|
|
||||||
(keyword (str/lower-case (.getColumnLabel rsmeta i))))
|
|
||||||
idxs)))
|
|
||||||
|
|
||||||
(defn as-lower-maps [^java.sql.ResultSet rs opts]
|
|
||||||
(let [rsmeta (.getMetaData rs)
|
(let [rsmeta (.getMetaData rs)
|
||||||
cols (get-lower-column-names rsmeta opts)]
|
cols (lower-case-cols rsmeta opts)]
|
||||||
(rs/->MapResultSetBuilder rs rsmeta cols)))
|
(rs/->MapResultSetBuilder rs rsmeta cols)))
|
||||||
|
|
||||||
(deftest test-map-row-builder
|
(deftest test-map-row-builder
|
||||||
|
|
@ -89,7 +86,52 @@
|
||||||
(testing "lower-case row builder"
|
(testing "lower-case row builder"
|
||||||
(let [row (p/-execute-one (ds)
|
(let [row (p/-execute-one (ds)
|
||||||
["select * from fruit where id = ?" 3]
|
["select * from fruit where id = ?" 3]
|
||||||
{:gen-fn as-lower-maps})]
|
{:gen-fn as-lower-case})]
|
||||||
(is (map? row))
|
(is (map? row))
|
||||||
(is (= 3 (:id row)))
|
(is (= 3 (:id row)))
|
||||||
(is (= "Peach" (:name row))))))
|
(is (= "Peach" (:name row))))))
|
||||||
|
|
||||||
|
(deftest test-mapify
|
||||||
|
(testing "no row builder is used"
|
||||||
|
(is (= [false]
|
||||||
|
(into [] (map map?) ; it is not a real map
|
||||||
|
(p/-execute (ds) ["select * from fruit where id = ?" 1]
|
||||||
|
{:gen-fn (constantly nil)}))))
|
||||||
|
(is (= ["Apple"]
|
||||||
|
(into [] (map :name) ; but keyword selection works
|
||||||
|
(p/-execute (ds) ["select * from fruit where id = ?" 1]
|
||||||
|
{:gen-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]
|
||||||
|
{:gen-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]
|
||||||
|
{:gen-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]
|
||||||
|
{:gen-fn (constantly nil)})))))
|
||||||
|
(testing "assoc and seq build maps"
|
||||||
|
(is (map? (reduce (fn [_ row] (reduced (assoc row :x 1)))
|
||||||
|
nil
|
||||||
|
(p/-execute (ds) ["select * from fruit"] {}))))
|
||||||
|
(is (seq? (reduce (fn [_ row] (reduced (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"] {})))))
|
||||||
|
(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))))
|
||||||
|
|
|
||||||
|
|
@ -85,33 +85,6 @@
|
||||||
(quick-bench ; simple keys, arrays -- 4.34-4.4
|
(quick-bench ; simple keys, arrays -- 4.34-4.4
|
||||||
(execute! con ["select * from fruit"] {:gen-fn rs/as-unqualified-arrays}))
|
(execute! con ["select * from fruit"] {:gen-fn rs/as-unqualified-arrays}))
|
||||||
|
|
||||||
(defn get-lower-column-names [^java.sql.ResultSetMetaData rsmeta opts]
|
|
||||||
(let [idxs (range 1 (inc (.getColumnCount rsmeta)))]
|
|
||||||
(mapv (fn [^Integer i]
|
|
||||||
(keyword (str/lower-case (.getColumnLabel rsmeta i))))
|
|
||||||
idxs)))
|
|
||||||
|
|
||||||
(defn as-lower-maps [^java.sql.ResultSet rs opts]
|
|
||||||
(let [rsmeta (.getMetaData rs)
|
|
||||||
cols (get-lower-column-names rsmeta opts)]
|
|
||||||
(next.jdbc.result-set/->MapResultSetBuilder rs rsmeta cols)))
|
|
||||||
|
|
||||||
(quick-bench ; simple keys -- 4.55-4.57
|
|
||||||
(execute! con ["select * from fruit"] {:gen-fn as-lower-maps}))
|
|
||||||
|
|
||||||
(defn lower-case-cols [^ResultSetMetaData rsmeta opts]
|
|
||||||
(mapv (fn [^Integer i]
|
|
||||||
(keyword (str/lower-case (.getColumnLabel rsmeta i))))
|
|
||||||
(range 1 (inc (.getColumnCount rsmeta)))))
|
|
||||||
|
|
||||||
(defn as-lower-case [^ResultSet rs opts]
|
|
||||||
(let [rsmeta (.getMetaData rs)
|
|
||||||
cols (lower-case-cols rsmeta opts)]
|
|
||||||
(next.jdbc.result-set/->MapResultSetBuilder rs rsmeta cols)))
|
|
||||||
|
|
||||||
(quick-bench
|
|
||||||
(execute! con ["SELECT * FROM fruit"] {:gen-fn as-lower-case}))
|
|
||||||
|
|
||||||
(quick-bench ; 9.5 -- 2x
|
(quick-bench ; 9.5 -- 2x
|
||||||
(jdbc/query {:connection con} ["select * from fruit"]))
|
(jdbc/query {:connection con} ["select * from fruit"]))
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue