diff --git a/src/next/jdbc/result_set.clj b/src/next/jdbc/result_set.clj index 52e790e..0f94f0e 100644 --- a/src/next/jdbc/result_set.clj +++ b/src/next/jdbc/result_set.clj @@ -386,11 +386,18 @@ (definterface MapifiedResultSet) (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] - "") + "Return the current 1-based row number, if available.") (column-names [this] - "")) + "Return a vector of the column names from the result set.")) (defn- mapify-result-set "Given a `ResultSet`, return an object that wraps the current row as a hash diff --git a/test/next/jdbc/result_set_test.clj b/test/next/jdbc/result_set_test.clj index 4fe148d..2843ff7 100644 --- a/test/next/jdbc/result_set_test.clj +++ b/test/next/jdbc/result_set_test.clj @@ -14,7 +14,7 @@ [next.jdbc.specs :as specs] [next.jdbc.test-fixtures :refer [with-test-db ds column default-options - mssql? mysql? postgres?]]) + derby? mssql? mysql? postgres?]]) (:import (java.sql ResultSet ResultSetMetaData))) (set! *warn-on-reflection* true) @@ -184,6 +184,73 @@ (is (= 3 ((column :FRUIT/id) 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 (testing "no row builder is used" (is (= [true] @@ -270,7 +337,7 @@ (defrecord Fruit [id name appearance cost grade]) -(defn fruit-builder [^ResultSet rs opts] +(defn fruit-builder [^ResultSet rs _] (reify rs/RowBuilder (->row [_] (->Fruit (.getObject rs "id")