Skip to content

Instantly share code, notes, and snippets.

@codeasone
Created January 15, 2019 15:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save codeasone/9a6cdfba83aabb84e02271b8e9964dec to your computer and use it in GitHub Desktop.
Save codeasone/9a6cdfba83aabb84e02271b8e9964dec to your computer and use it in GitHub Desktop.
Coercion of namespaced keywords to and from postgresql enumerated types
(ns clj-postgresql.enum
"Coerce Clojure keywords of the form :type/value to Postgres ENUM"
(:require [clj-postgresql.core :as pg]
[clojure.java.jdbc :as jdbc :refer [sql-value]]
[clojure.string :as s]
[clojure.test :refer :all])
(:import org.postgresql.util.PGobject))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Writing
;;
(defn kw->pgenum [kw]
(let [type (-> (namespace kw)
(s/replace "-" "_"))
value (name kw)]
(doto (PGobject.)
(.setType type)
(.setValue value))))
(extend-type clojure.lang.Keyword
jdbc/ISQLValue
(sql-value [kw]
(kw->pgenum kw)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Reading
;;
(def +schema-enums+
"A set of all PostgreSQL enums in schema.sql. Used to convert
enum-values back into Clojure keywords."
#{"processing_status"})
(extend-type String
jdbc/IResultSetReadColumn
(result-set-read-column [val rsmeta idx]
(let [type (.getColumnTypeName rsmeta idx)]
(if (contains? +schema-enums+ type)
(keyword (s/replace type "_" "-") val)
val))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Tests
;;
(deftest enum-handling-writing-to-db
(testing "when a valid value is used"
(testing "coercion of namespaced keywords to the DB works"
(jdbc/with-db-transaction [tx (pg/spec)]
(jdbc/insert! tx :files {:name "my-file.txt", :status :processing-status/pending}))
))
(testing "when an invalid value is used"
(testing "coercion of namespaced keywords to the DB fail"
(jdbc/with-db-transaction [tx (pg/spec)]
(is (thrown? org.postgresql.util.PSQLException
(jdbc/insert! tx :files {:name "my-file.txt", :status :processing-status/unsupported}))))
)))
(deftest enum-handling-reading-from-db
(testing "coercion of string values for enumerated types in DB to keywords works"
(jdbc/with-db-transaction [tx (pg/spec)]
(is (= (first (jdbc/query tx ["SELECT * FROM files"]))
{:status :processing-status/pending, :name "my-file.txt"})))
))
;; Credits: https://www.bevuta.com/en/blog/using-postgresql-enums-in-clojure/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment