Skip to content

Instantly share code, notes, and snippets.

@theasp
Created December 15, 2017 20:08
Show Gist options
  • Save theasp/8b1c35134788ba88edca12f52ad75f27 to your computer and use it in GitHub Desktop.
Save theasp/8b1c35134788ba88edca12f52ad75f27 to your computer and use it in GitHub Desktop.
Using `pg` from ClojureScript on Node.js with an interface like `postgres.async`
(ns hello-world.pg
(:require
[clojure.string :as string]
[cljs.nodejs :as nodejs]
[cljs.core.async
:refer [<! chan put! close! onto-chan to-chan]]
[taoensso.timbre :as timbre
:refer-macros [tracef debugf infof warnf errorf]])
(:require-macros
[cljs.core.async.macros :as m :refer [go]]))
(defn result-chan [f & args]
(let [channel (chan 1)
callback (fn [rs err]
(put! channel (or err rs))
(close! channel))]
(apply f (concat args [callback]))
channel))
(defn open-db
"Creates a db connection"
[{:keys [hostname port username password database
pool-size ssl validation-query pipeline]
:as config}]
(doseq [param [:hostname :username :password :database]]
(when (nil? (param config))
(errorf (str param " is required"))
(throw (new js/Error (str param " is required")))))
(-> {"host" hostname
"port" (or port 5432)
"user" username
"database" database
"password" password
"ssl" (boolean ssl)}
(clj->js)
(pg.Client.)))
(defn open-pool
"Creates a db connection pool"
[{:keys [hostname port username password database
pool-size ssl validation-query pipeline]
:as config}]
(doseq [param [:hostname :username :password :database]]
(when (nil? (param config))
(errorf (str param " is required"))
(throw (new js/Error (str param " is required")))))
(-> {"host" hostname
"port" (or port 5432)
"user" username
"database" database
"password" password
"ssl" (boolean ssl)
"max" (or pool-size 20)
"idleTimeoutMillis" 30000}
(clj->js)
(pg.Pool.)))
(defn close-db!
"Closes a db connection pool"
[db]
(.close db))
(defn connect!
([db]
(result-chan connect! db))
([db f]
(.connect db
(fn [err client done]
(f [client done (some-> err (sql-error "CONNECT"))] nil)))))
(defn execute!
"Executes an sql statement with parameters and returns result rows and update count."
([db sql]
(result-chan execute! db sql))
([db [sql & params] f]
#_(debugf "SQL: %s %s" sql (vec params))
(try
(.query db sql
(clj->js (vec params))
(fn [err rs]
#_(debugf "Execute result: %s" {:err err :rs rs})
(if (some? err)
(f nil (sql-error err sql))
(f (result->map rs) nil))))
(catch js/Object err
(f nil (sql-error err sql))))))
(defn query!
"Executes an sql query with parameters and returns result rows."
([db sql]
(result-chan query! db sql))
([db sql f]
(execute! db sql f)))
(defn insert!
"Executes an sql insert and returns update count and returned rows.
Spec format is
:table - table name
:returning - sql string"
([db sql-spec data]
(result-chan insert! db sql-spec data))
([db sql-spec data f]
(execute! db (list* (create-insert-sql sql-spec data)
(if (map? data)
(vals data)
(flatten (map vals data))))
f)))
(defn update!
"Executes an sql update and returns update count and returned rows.
Spec format is
:table - table name
:returning - sql string
:where - [sql & params]"
([db sql-spec data]
(result-chan update! db sql-spec data))
([db sql-spec data f]
(execute! db (concat [(create-update-sql sql-spec data)]
(rest (:where sql-spec))
(vals data))
f)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment