(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")})) (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))))))