new database
This commit is contained in:
+196
@@ -0,0 +1,196 @@
|
||||
(ns conman.core
|
||||
(:require [clojure.java.io :as io]
|
||||
[clojure.set :refer [rename-keys]]
|
||||
[hikari-cp.core :refer [make-datasource]]
|
||||
[hugsql.core :as hugsql]
|
||||
[hugsql.adapter.next-jdbc :as next-adapter]
|
||||
[to-jdbc-uri.core :refer [to-jdbc-uri]])
|
||||
(:import [clojure.lang IDeref]))
|
||||
|
||||
(hugsql/set-adapter! (next-adapter/hugsql-adapter-next-jdbc))
|
||||
|
||||
(defn validate-files [filenames]
|
||||
(doseq [file filenames]
|
||||
(when-not (or (instance? java.io.File file) (io/resource file))
|
||||
(throw (Exception. (str "conman could not find the query file:" file))))))
|
||||
|
||||
(defn try-snip [[id snip]]
|
||||
[id
|
||||
(update snip :fn
|
||||
(fn [snip]
|
||||
(fn [& args]
|
||||
(try (apply snip args)
|
||||
(catch Exception e
|
||||
(throw (ex-info (ex-message e) {:snip-id id} e)))))))])
|
||||
|
||||
(defn try-query [[id query]]
|
||||
[id
|
||||
(update query :fn
|
||||
(fn [query]
|
||||
(fn
|
||||
([conn params]
|
||||
(try (query conn params)
|
||||
(catch Exception e
|
||||
(throw (ex-info (ex-message e) {:query-id id} e)))))
|
||||
([conn params opts & command-opts]
|
||||
(try (apply query conn params opts command-opts)
|
||||
(catch Exception e
|
||||
(throw (ex-info (ex-message e) {:query-id id} e))))))))])
|
||||
|
||||
(defn load-queries [& args]
|
||||
(let [options? (map? (first args))
|
||||
options (if options? (first args) {})
|
||||
filenames (if options? (rest args) args)]
|
||||
(validate-files filenames)
|
||||
(reduce
|
||||
(fn [queries file]
|
||||
(let [{snips true
|
||||
fns false}
|
||||
(group-by
|
||||
#(-> % second :meta :snip? boolean)
|
||||
(hugsql/map-of-db-fns file options))]
|
||||
(-> queries
|
||||
(update :snips (fnil into {}) (mapv try-snip snips))
|
||||
(update :fns (fnil into {}) (mapv try-query fns)))))
|
||||
{}
|
||||
filenames)))
|
||||
|
||||
(defn intern-fn [ns id meta f]
|
||||
(intern ns (with-meta (symbol (name id)) meta) f))
|
||||
|
||||
(defmacro bind-connection [conn & filenames]
|
||||
`(let [{snips# :snips fns# :fns :as queries#} (conman.core/load-queries ~@filenames)]
|
||||
(doseq [[id# {fn# :fn meta# :meta}] snips#]
|
||||
(conman.core/intern-fn *ns* id# meta# fn#))
|
||||
(doseq [[id# {query# :fn meta# :meta}] fns#]
|
||||
(conman.core/intern-fn *ns* id#
|
||||
;; Need to explicitly set :arglists since we don't use defn.
|
||||
;; Another option would be to generate defns.
|
||||
(assoc meta#
|
||||
:arglists (quote ~'([] [params] [db params options & command-options])))
|
||||
(fn f#
|
||||
([] (query# ~conn {}))
|
||||
([params#] (query# ~conn params#))
|
||||
([conn# params# & args#] (apply query# conn# params# args#)))))
|
||||
queries#))
|
||||
|
||||
(defmacro bind-connection-deref [conn & filenames]
|
||||
`(let [{snips# :snips fns# :fns :as queries#} (conman.core/load-queries ~@filenames)]
|
||||
(doseq [[id# {fn# :fn meta# :meta}] snips#]
|
||||
(conman.core/intern-fn *ns* id# meta# fn#))
|
||||
(doseq [[id# {query# :fn meta# :meta}] fns#]
|
||||
(conman.core/intern-fn *ns* id#
|
||||
(assoc meta#
|
||||
:arglists (quote ~'([] [params] [db params options & command-options])))
|
||||
(fn f#
|
||||
([] (query# (deref ~conn) {}))
|
||||
([params#] (query# (deref ~conn) params#))
|
||||
([conn# params# & args#] (apply query# conn# params# args#)))))
|
||||
queries#))
|
||||
|
||||
(defn bind-connection-map [conn & args]
|
||||
(-> (apply load-queries args)
|
||||
(update :snips
|
||||
(fn [snips]
|
||||
(reduce (fn [acc [id snip]] (assoc acc id snip)) {} snips)))
|
||||
(update :fns
|
||||
(fn [queries]
|
||||
(reduce
|
||||
(fn [acc [id query]]
|
||||
(assoc acc id
|
||||
(update query
|
||||
:fn
|
||||
(fn [query]
|
||||
(fn fn#
|
||||
([] (query conn {}))
|
||||
([params]
|
||||
(query conn params))
|
||||
([conn params & args] (apply query conn params args)))))))
|
||||
{}
|
||||
queries)))))
|
||||
|
||||
(defn find-fn [connection-map query-type k]
|
||||
(or (get-in connection-map [query-type k :fn])
|
||||
(throw (IllegalArgumentException.
|
||||
(str (if (= query-type :snips) "no snippet" "no query")
|
||||
" found for the key: " k
|
||||
"', available queries: " (keys (get connection-map query-type)))))))
|
||||
|
||||
(defn snip [connection-map snip-key & args]
|
||||
"runs a SQL query snippet
|
||||
queries - a map of queries
|
||||
id - keyword indicating the name of the query
|
||||
args - arguments that will be passed to the query"
|
||||
(apply (find-fn connection-map :snips snip-key) args))
|
||||
|
||||
(defn query
|
||||
"runs a database query and returns the result
|
||||
conn - database connection
|
||||
queries - a map of queries
|
||||
id - keyword indicating the name of the query
|
||||
args - arguments that will be passed to the query"
|
||||
([connection-map query-key]
|
||||
((find-fn connection-map :fns query-key)))
|
||||
([connection-map query-key params]
|
||||
((find-fn connection-map :fns query-key) params))
|
||||
([conn connection-map query-key params & opts]
|
||||
(apply (find-fn connection-map :fns query-key) conn params opts)))
|
||||
|
||||
(defn- format-url [pool-spec]
|
||||
(if (:jdbc-url pool-spec)
|
||||
(update pool-spec :jdbc-url to-jdbc-uri)
|
||||
pool-spec))
|
||||
|
||||
(defn make-config [{:keys [jdbc-url adapter datasource datasource-classname] :as pool-spec}]
|
||||
(when (not (or jdbc-url adapter datasource datasource-classname))
|
||||
(throw (Exception. "one of :jdbc-url, :adapter, :datasource, or :datasource-classname is required to initialize the connection!")))
|
||||
(-> pool-spec
|
||||
(format-url)
|
||||
(rename-keys
|
||||
{:auto-commit? :auto-commit
|
||||
:conn-timeout :connection-timeout
|
||||
:min-idle :minimum-idle
|
||||
:max-pool-size :maximum-pool-size})))
|
||||
|
||||
(defn connect!
|
||||
"attempts to create a new connection and set it as the value of the conn atom,
|
||||
does nothing if conn atom is already populated"
|
||||
[pool-spec]
|
||||
(make-datasource (make-config pool-spec)))
|
||||
|
||||
(defn disconnect!
|
||||
"checks if there's a connection and closes it
|
||||
resets the conn to nil"
|
||||
[conn]
|
||||
(when (and (instance? com.zaxxer.hikari.HikariDataSource conn)
|
||||
(not (.isClosed conn)))
|
||||
(.close conn)))
|
||||
|
||||
(defn reconnect!
|
||||
"calls disconnect! to ensure the connection is closed
|
||||
then calls connect! to establish a new connection"
|
||||
[conn pool-spec]
|
||||
(disconnect! conn)
|
||||
(connect! pool-spec))
|
||||
|
||||
(extend-protocol next.jdbc.protocols/Sourceable
|
||||
IDeref
|
||||
(get-datasource [this]
|
||||
(next.jdbc.protocols/get-datasource (deref this))))
|
||||
|
||||
(defmacro with-transaction
|
||||
"Runs the body in a transaction where t-conn is the name of the transaction connection.
|
||||
The body will be evaluated within a binding where conn is set to the transactional
|
||||
connection. The isolation level and readonly status of the transaction may also be specified.
|
||||
(with-transaction [conn {:isolation level :read-only? true}]
|
||||
... t-conn ...)
|
||||
See next.jdbc/transact for more details on the semantics of the :isolation and
|
||||
:read-only options."
|
||||
[[dbsym & opts] & body]
|
||||
`(if (instance? IDeref ~dbsym)
|
||||
(next.jdbc/with-transaction [t-conn# (deref ~dbsym) ~@opts]
|
||||
(binding [~dbsym (delay t-conn#)]
|
||||
~@body))
|
||||
(next.jdbc/with-transaction [t-conn# ~dbsym ~@opts]
|
||||
(binding [~dbsym t-conn#]
|
||||
~@body))))
|
||||
Reference in New Issue
Block a user