Skip to content
Snippets Groups Projects
db.clj 2.79 KiB
Newer Older
(ns document-storage.postgres.db
  (:require
   [conman.core :as conman]
   [clojure.string :as str]
   [hugsql.core :as hugsql]
   [clojure.repl]
   [clojure.java.jdbc :as jdbc]
   [next.jdbc.prepare :as p]
   [next.jdbc.result-set :as rs])
  (:import
   [org.postgresql.util PGobject]
   [java.sql Timestamp]
   [java.sql Date Timestamp PreparedStatement]))

(defn get-database-url [config]
  (or (get config :jdbc-url)
      (str "jdbc:postgresql://"
           (get config :database-host) ":"
           (get config :database-port) "/"
           (get config :database-name) "?"
           "user=" (get config :database-user) "&"
           "password=" (get config :database-password))))


(defn connect! [config]
  (let [connection (conman/connect! (merge config
                                           {:jdbc-url (get-database-url config)}))]
    {:connection connection
     :queries (conman/bind-connection-map
               connection
               "sql/_ds_postgres.sql"
               "sql/_ds_postgres_repository.sql"
               "sql/_ds_postgres_storage.sql"
               "sql/_ds_postgres_cache.sql"
               "sql/_ds_postgres_document_stats.sql")}))

(defn disconnect! [connection]
  (.close connection)
  (conman/disconnect! connection))


(defn kw->pgenum [kw]
  (let [type (-> (namespace kw)
                 (str/replace "-" "_"))
        value (name kw)]
    (doto (PGobject.)
      (.setType type)
      (.setValue value))))

(defn pg-param-type
  [^PreparedStatement s ^long idx]
  (if-let [md (.getParameterMetaData s)]
    (or (.getParameterTypeName md idx)
        (throw (ex-info "We could not obtain the column type name" {:got s :meta md})))
    (throw (ex-info "We could not obtain metadata from the prepared statement" {:got s}))))


(defn <-pgobject
  "Transform PGobject containing `json` or `jsonb` value to Clojure
  data."
  [^org.postgresql.util.PGobject v]
  (let [type  (.getType v)
        value (.getValue v)]
    (cond
      (str/includes? type "ds_encoding") (keyword "ds-encoding" value)
      (str/includes? type "ds_type") (keyword "ds-type" value)
      :else value)))

(extend-protocol rs/ReadableColumn
  org.postgresql.util.PGobject
  (read-column-by-label [^org.postgresql.util.PGobject v _]
    (<-pgobject v))
  (read-column-by-index [^org.postgresql.util.PGobject v _2 _3]
    (<-pgobject v)))

(extend-protocol p/SettableParameter
  clojure.lang.Keyword
  (set-parameter [^clojure.lang.Keyword v ^PreparedStatement ps ^long i]
    (.setObject ps i (kw->pgenum v)))
  java.util.Date
  (set-parameter [^java.util.Date v ^PreparedStatement stmt ^long idx]
    (.setObject stmt idx
                (case (pg-param-type stmt idx)
                  "date" (Date. (.getTime v))
                  "timestamp" (Timestamp. (.getTime v))
                  "timestamptz" (Timestamp. (.getTime v))))))