Addresses #110 by adding tests around this scenario
This commit is contained in:
parent
860298943c
commit
b0b9e53352
2 changed files with 79 additions and 5 deletions
|
|
@ -386,11 +386,18 @@
|
||||||
(definterface MapifiedResultSet)
|
(definterface MapifiedResultSet)
|
||||||
|
|
||||||
(defprotocol InspectableMapifiedResultSet :extend-via-metadata true
|
(defprotocol InspectableMapifiedResultSet :extend-via-metadata true
|
||||||
""
|
"Protocol for exposing aspects of the (current) result set via functions.
|
||||||
|
|
||||||
|
The intent here is to expose information that is associated with either
|
||||||
|
the (current row of the) result set or the result set metadata, via
|
||||||
|
functions that can be called inside a reducing function being used over
|
||||||
|
`next.jdbc/plan`, including situations where the reducing function has
|
||||||
|
to realize a row by calling `datafiable-row` but still wants to call
|
||||||
|
these functions on the (realized) row."
|
||||||
(row-number [this]
|
(row-number [this]
|
||||||
"")
|
"Return the current 1-based row number, if available.")
|
||||||
(column-names [this]
|
(column-names [this]
|
||||||
""))
|
"Return a vector of the column names from the result set."))
|
||||||
|
|
||||||
(defn- mapify-result-set
|
(defn- mapify-result-set
|
||||||
"Given a `ResultSet`, return an object that wraps the current row as a hash
|
"Given a `ResultSet`, return an object that wraps the current row as a hash
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
[next.jdbc.specs :as specs]
|
[next.jdbc.specs :as specs]
|
||||||
[next.jdbc.test-fixtures :refer [with-test-db ds column
|
[next.jdbc.test-fixtures :refer [with-test-db ds column
|
||||||
default-options
|
default-options
|
||||||
mssql? mysql? postgres?]])
|
derby? mssql? mysql? postgres?]])
|
||||||
(:import (java.sql ResultSet ResultSetMetaData)))
|
(:import (java.sql ResultSet ResultSetMetaData)))
|
||||||
|
|
||||||
(set! *warn-on-reflection* true)
|
(set! *warn-on-reflection* true)
|
||||||
|
|
@ -184,6 +184,73 @@
|
||||||
(is (= 3 ((column :FRUIT/id) row)))
|
(is (= 3 ((column :FRUIT/id) row)))
|
||||||
(is (= "Peach" ((column :FRUIT/name) row))))))
|
(is (= "Peach" ((column :FRUIT/name) row))))))
|
||||||
|
|
||||||
|
(deftest test-row-number
|
||||||
|
;; two notes here: we use as-arrays as a nod to issue #110 to make
|
||||||
|
;; sure that actually works; also Apache Derby is the only database
|
||||||
|
;; (that we test against) to restrict .getRow() calls to scroll cursors
|
||||||
|
(testing "row-numbers on bare abstraction"
|
||||||
|
(is (= [1 2 3]
|
||||||
|
(into [] (map rs/row-number)
|
||||||
|
(p/-execute (ds) ["select * from fruit where id < ?" 4]
|
||||||
|
;; we do not need a real builder here...
|
||||||
|
(cond-> {:builder-fn (constantly nil)}
|
||||||
|
(derby?)
|
||||||
|
(assoc :concurrency :read-only
|
||||||
|
:cursors :close
|
||||||
|
:result-type :scroll-insensitive)))))))
|
||||||
|
(testing "row-numbers on realized row"
|
||||||
|
(is (= [1 2 3]
|
||||||
|
(into [] (comp (map #(rs/datafiable-row % (ds) {}))
|
||||||
|
(map rs/row-number))
|
||||||
|
(p/-execute (ds) ["select * from fruit where id < ?" 4]
|
||||||
|
;; ...but datafiable-row requires a real builder
|
||||||
|
(cond-> {:builder-fn rs/as-arrays}
|
||||||
|
(derby?)
|
||||||
|
(assoc :concurrency :read-only
|
||||||
|
:cursors :close
|
||||||
|
:result-type :scroll-insensitive))))))))
|
||||||
|
|
||||||
|
(deftest test-column-names
|
||||||
|
(testing "column-names on bare abstraction"
|
||||||
|
(is (= #{"id" "appearance" "grade" "cost" "name"}
|
||||||
|
(reduce (fn [_ row]
|
||||||
|
(-> row
|
||||||
|
(->> (rs/column-names)
|
||||||
|
(map (comp str/lower-case name))
|
||||||
|
(set)
|
||||||
|
(reduced))))
|
||||||
|
nil
|
||||||
|
(p/-execute (ds) ["select * from fruit where id < ?" 4]
|
||||||
|
;; column-names require a real builder
|
||||||
|
{:builder-fn rs/as-arrays})))))
|
||||||
|
(testing "column-names on realized row"
|
||||||
|
(is (= #{"id" "appearance" "grade" "cost" "name"}
|
||||||
|
(reduce (fn [_ row]
|
||||||
|
(-> row
|
||||||
|
(rs/datafiable-row (ds) {})
|
||||||
|
(->> (rs/column-names)
|
||||||
|
(map (comp str/lower-case name))
|
||||||
|
(set)
|
||||||
|
(reduced))))
|
||||||
|
nil
|
||||||
|
(p/-execute (ds) ["select * from fruit where id < ?" 4]
|
||||||
|
{:builder-fn rs/as-arrays}))))))
|
||||||
|
|
||||||
|
(deftest test-over-partition-all
|
||||||
|
;; this verifies that InspectableMapifiedResultSet survives partition-all
|
||||||
|
(testing "row-numbers on partitioned rows"
|
||||||
|
(is (= [[1 2] [3 4]]
|
||||||
|
(into [] (comp (map #(rs/datafiable-row % (ds) %))
|
||||||
|
(partition-all 2)
|
||||||
|
(map #(map rs/row-number %)))
|
||||||
|
(p/-execute (ds) ["select * from fruit"]
|
||||||
|
;; we do not need a real builder here...
|
||||||
|
(cond-> {:builder-fn rs/as-arrays}
|
||||||
|
(derby?)
|
||||||
|
(assoc :concurrency :read-only
|
||||||
|
:cursors :close
|
||||||
|
:result-type :scroll-insensitive))))))))
|
||||||
|
|
||||||
(deftest test-mapify
|
(deftest test-mapify
|
||||||
(testing "no row builder is used"
|
(testing "no row builder is used"
|
||||||
(is (= [true]
|
(is (= [true]
|
||||||
|
|
@ -270,7 +337,7 @@
|
||||||
|
|
||||||
(defrecord Fruit [id name appearance cost grade])
|
(defrecord Fruit [id name appearance cost grade])
|
||||||
|
|
||||||
(defn fruit-builder [^ResultSet rs opts]
|
(defn fruit-builder [^ResultSet rs _]
|
||||||
(reify
|
(reify
|
||||||
rs/RowBuilder
|
rs/RowBuilder
|
||||||
(->row [_] (->Fruit (.getObject rs "id")
|
(->row [_] (->Fruit (.getObject rs "id")
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue