Skip to content

Instantly share code, notes, and snippets.

@zelark
Last active February 2, 2024 19:47
Show Gist options
  • Star 14 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save zelark/3b484e9b16ad55c97b4ed6f6ea13986b to your computer and use it in GitHub Desktop.
Save zelark/3b484e9b16ad55c97b4ed6f6ea13986b to your computer and use it in GitHub Desktop.
Support json and jsonb Postgres types in Clojure.
;; For supporting more PG types, see https://github.com/remodoy/clj-postgresql
(ns pg-test.types
(:require [cheshire.core :as json]
[clojure.java.jdbc :as jdbc])
(:import [org.postgresql.util PGobject]
[java.sql PreparedStatement]))
;; Writing
(defn- to-pg-json [data json-type]
(doto (PGobject.)
(.setType (name json-type))
(.setValue (json/generate-string data))))
(defn parameter-dispatch-fn [_ type-name]
(keyword type-name))
(defmulti map->parameter parameter-dispatch-fn)
(defmethod map->parameter :json
[m _]
(to-pg-json m :json))
(defmethod map->parameter :jsonb
[m _]
(to-pg-json m :jsonb))
(extend-protocol jdbc/ISQLParameter
;; Convert Clojure maps to SQL parameter values
clojure.lang.IPersistentMap
(set-parameter [m ^PreparedStatement s ^long i]
(let [meta (.getParameterMetaData s)]
(if-let [type-name (keyword (.getParameterTypeName meta i))]
(.setObject s i (map->parameter m type-name))
(.setObject s i m)))))
;; Reading
(defmulti read-pgobject
"Convert returned PGobject to Clojure value."
#(keyword (when (some? %) (.getType ^PGobject %))))
(defmethod read-pgobject :json
[^PGobject x]
(when-let [val (.getValue x)]
(json/parse-string val true)))
(defmethod read-pgobject :jsonb
[^PGobject x]
(when-let [val (.getValue x)]
(json/parse-string val true)))
(defmethod read-pgobject :default
[^PGobject x]
(.getValue x))
;; Extend clojure.java.jdbc's protocol for interpreting ResultSet column values.
(extend-protocol jdbc/IResultSetReadColumn
;; PGobjects have their own multimethod
PGobject
(result-set-read-column [val _ _]
(read-pgobject val)))
;; How to use:
;; (require 'pg-test.types)
;; (require '[clojure.java.jdbc :as jdbc])
;;
;; (jdbc/query conn ["select data::json from testing"])
@endvvell
Copy link

Thanks, this helped a lot!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment