Document PGInterval to Duration handling for postgres

Postgres supports a non-standard Interval type which is implemented as
the PGInterval type in the postgres jdbc driver. Many users would likely
want to deal with intervals as the standard java.time.Duration type. How
this can be done is documented under tips & tricks.

Signed-off-by: Snorre Magnus Davøen <post@snorre.io>
This commit is contained in:
Snorre Magnus Davøen 2021-04-25 13:35:20 +02:00
parent 91dda2cdae
commit fed6e671f9
No known key found for this signature in database
GPG key ID: 55068DE619690243

View file

@ -202,6 +202,54 @@ In addition, if you want `java.time.Instant`, `java.time.LocalDate`, and `java.t
`next.jdbc.date-time` also includes functions that you can call at application startup to extend `ReadableColumn` to either return `java.time.Instant` or `java.time.LocalDate`/`java.time.LocalDateTime` (as well as a function to restore the default behavior of returning `java.sql.Date` and `java.sql.Timestamp`).
### Working with Interval
Postgres has a nonstandard SQL type Interval that is implemented in the Postgres driver as the `org.postgresql.util.PGInterval` type.
In many cases you would want to work with intervals as `java.time.Duration` type by default.
You can support `Duration` instances by extending `SettableParameter` to the `java.time.Duration` type.
Conversely you can support converting PGIntervals back to Durations by extending `ReadableColumn` to the `org.postgresql.util.PGInterval` type.
```clojure
(import '[org.postgresql.util PGInterval])
(import '[java.sql PreparedStatement])
(import '[java.time Duration])
(require '[next.jdbc.result-set :as rs])
(require '[next.jdbc.prepare :as p])
(defn ->pg-interval
"Takes a Dudration instance and converts it into a PGInterval
instance where the interval is created as a number of seconds."
[^java.time.Duration duration]
(doto (PGInterval.)
(.setSeconds (.getSeconds duration))))
(extend-protocol p/SettableParameter
;; Convert durations to PGIntervals before inserting into db
java.time.Duration
(set-parameter [^java.time.Duration v ^PreparedStatement s ^long i]
(.setObject s i (->pg-interval v))))
(defn <-pg-interval
"Takes a PGInterval instance and converts it into a Duration
instance. Ignore sub-second units."
[^org.postgresql.util.PGInterval interval]
(-> Duration/ZERO
(.plusSeconds (.getSeconds interval))
(.plusMinutes (.getMinutes interval))
(.plusHours (.getHours interval))
(.plusDays (.getDays interval))))
(extend-protocol rs/ReadableColumn
;; Convert PGIntervals back to durations
org.postgresql.util.PGInterval
(read-column-by-label [^org.postgresql.util.PGInterval v _]
(<-pg-interval v))
(read-column-by-index [^org.postgresql.util.PGInterval v _2 _3]
(<-pg-interval v)))
```
### Working with Enumerated Types
PostgreSQL has a SQL extension for defining enumerated types and the default `set-parameter` implementation will not work for those. You can use `next.jdbc.types/as-other` to wrap string values in a way that the JDBC driver will convert them to enumerated type values: