From c27e9a57e0120f53a0a2d81734babe14479d8f03 Mon Sep 17 00:00:00 2001 From: Eric Ihli Date: Tue, 21 Jul 2020 12:40:18 -0700 Subject: [PATCH] Extend ReadableColumn protocol with more rsmeta There is a use case for needing access to ResultSetMetaData inside read-column-by-label. SQLite doesn't have a boolean affinity. It stores booleans as integers 0 or 1, so that's what the JDBC driver gives us, and without access to ResultSetMetaData we have no way of knowing the intended affinity of the column that the value came from. But by looking at `getColumnTypeName` on the ResultSetMetaData and seeing if it's "BOOL", we can perform the conversion in the ReadableColumn implementations. --- doc/result-set-builders.md | 4 ++++ doc/tips-and-tricks.md | 7 +++++++ src/next/jdbc/date_time.clj | 6 ++++++ src/next/jdbc/result_set.clj | 12 +++++++++--- 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/doc/result-set-builders.md b/doc/result-set-builders.md index bf3ced7..72012e3 100644 --- a/doc/result-set-builders.md +++ b/doc/result-set-builders.md @@ -138,11 +138,15 @@ The default implementation of this protocol is for these two functions to return java.sql.Date (read-column-by-label [^java.sql.Date v _] (.toLocalDate v)) + (read-column-by-label [^java.sql.Date v _2 _3] + (.toLocalDate v)) (read-column-by-index [^java.sql.Date v _2 _3] (.toLocalDate v)) java.sql.Timestamp (read-column-by-label [^java.sql.Timestamp v _] (.toInstant v)) + (read-column-by-label [^java.sql.Timestamp v _2 _3] + (.toInstant v)) (read-column-by-index [^java.sql.Timestamp v _2 _3] (.toInstant v))) ``` diff --git a/doc/tips-and-tricks.md b/doc/tips-and-tricks.md index 3ece69b..9121d8d 100644 --- a/doc/tips-and-tricks.md +++ b/doc/tips-and-tricks.md @@ -11,6 +11,8 @@ Columns declared with the `CLOB` or `BLOB` SQL types are typically rendered into java.sql.Clob (read-column-by-label [^java.sql.Clob v _] (with-open [rdr (.getCharacterStream v)] (slurp rdr))) + (read-column-by-label [^java.sql.Clob v _2 _3] + (with-open [rdr (.getCharacterStream v)] (slurp rdr))) (read-column-by-index [^java.sql.Clob v _2 _3] (with-open [rdr (.getCharacterStream v)] (slurp rdr)))) ``` @@ -22,6 +24,8 @@ There is a helper in `next.jdbc.result-set` to make this easier -- `clob->string java.sql.Clob (read-column-by-label [^java.sql.Clob v _] (clob->string v)) + (read-column-by-label [^java.sql.Clob v _2 _3_] + (clob->string v)) (read-column-by-index [^java.sql.Clob v _2 _3] (clob->string v))) ``` @@ -145,6 +149,7 @@ ResultSet protocol extension to read SQL arrays as Clojure vectors. (extend-protocol rs/ReadableColumn Array (read-column-by-label [^Array v _] (vec (.getArray v))) + (read-column-by-label [^Array v _ _] (vec (.getArray v))) (read-column-by-index [^Array v _ _] (vec (.getArray v)))) ``` @@ -272,6 +277,8 @@ Finally we extend `next.jdbc.prepare/SettableParameter` and `next.jdbc.result-se org.postgresql.util.PGobject (read-column-by-label [^org.postgresql.util.PGobject v _] (<-pgobject v)) + (read-column-by-label [^org.postgresql.util.PGobject v _2 _3] + (<-pgobject v)) (read-column-by-index [^org.postgresql.util.PGobject v _2 _3] (<-pgobject v))) ``` diff --git a/src/next/jdbc/date_time.clj b/src/next/jdbc/date_time.clj index 642d767..a9b69d5 100644 --- a/src/next/jdbc/date_time.clj +++ b/src/next/jdbc/date_time.clj @@ -72,9 +72,11 @@ (extend-protocol rs/ReadableColumn java.sql.Date (read-column-by-label [^java.sql.Date v _] v) + (read-column-by-label [^java.sql.Date v _2 _3] v) (read-column-by-index [^java.sql.Date v _2 _3] v) java.sql.Timestamp (read-column-by-label [^java.sql.Timestamp v _] (.toInstant v)) + (read-column-by-label [^java.sql.Timestamp v _2 _3] (.toInstant v)) (read-column-by-index [^java.sql.Timestamp v _2 _3] (.toInstant v)))) (defn read-as-local @@ -86,9 +88,11 @@ (extend-protocol rs/ReadableColumn java.sql.Date (read-column-by-label [^java.sql.Date v _] (.toLocalDate v)) + (read-column-by-label [^java.sql.Date v _2 _3] (.toLocalDate v)) (read-column-by-index [^java.sql.Date v _2 _3] (.toLocalDate v)) java.sql.Timestamp (read-column-by-label [^java.sql.Timestamp v _] (.toLocalDateTime v)) + (read-column-by-label [^java.sql.Timestamp v _2 _3] (.toLocalDateTime v)) (read-column-by-index [^java.sql.Timestamp v _2 _3] (.toLocalDateTime v)))) (defn read-as-default @@ -100,7 +104,9 @@ (extend-protocol rs/ReadableColumn java.sql.Date (read-column-by-label [^java.sql.Date v _] v) + (read-column-by-label [^java.sql.Date v _2 _3] v) (read-column-by-index [^java.sql.Date v _2 _3] v) java.sql.Timestamp (read-column-by-label [^java.sql.Timestamp v _] v) + (read-column-by-label [^java.sql.Timestamp v _2 _3] v) (read-column-by-index [^java.sql.Timestamp v _2 _3] v))) diff --git a/src/next/jdbc/result_set.clj b/src/next/jdbc/result_set.clj index 8c41ae2..cd40dd1 100644 --- a/src/next/jdbc/result_set.clj +++ b/src/next/jdbc/result_set.clj @@ -102,7 +102,9 @@ `Boolean` implementation ensures a canonicalized `true`/`false` value, but it can be extended to provide custom behavior for special types. Extension via metadata is supported." - (read-column-by-label [val label] + (read-column-by-label + [val label] + [val rsmeta label] "Function for transforming values after reading them via a column label.") (read-column-by-index [val rsmeta idx] "Function for transforming values after reading them via a column index.")) @@ -110,14 +112,17 @@ (extend-protocol ReadableColumn Object (read-column-by-label [x _] x) + (read-column-by-label [x _2 _3] x) (read-column-by-index [x _2 _3] x) Boolean (read-column-by-label [x _] (if (= true x) true false)) + (read-column-by-label [x _2 _3] (if (= true x) true false)) (read-column-by-index [x _2 _3] (if (= true x) true false)) nil (read-column-by-label [_1 _2] nil) + (read-column-by-label [_1 _2 _3] nil) (read-column-by-index [_1 _2 _3] nil)) (defprotocol RowBuilder @@ -513,6 +518,7 @@ (try (clojure.lang.MapEntry. k (read-column-by-label (.getObject rs (name k)) + (:rsmeta @builder) (name k))) (catch SQLException _))) @@ -535,14 +541,14 @@ (if (number? k) (let [^Integer i (inc k)] (read-column-by-index (.getObject rs i) (:rsmeta @builder) i)) - (read-column-by-label (.getObject rs (name k)) (name k))) + (read-column-by-label (.getObject rs (name k)) (:rsmeta @builder) (name k))) (catch SQLException _))) (valAt [this k not-found] (try (if (number? k) (let [^Integer i (inc k)] (read-column-by-index (.getObject rs i) (:rsmeta @builder) i)) - (read-column-by-label (.getObject rs (name k)) (name k))) + (read-column-by-label (.getObject rs (name k)) (:rsmeta @builder) (name k))) (catch SQLException _ not-found)))