Skip to content

Subproject split and dependency updates #26

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 13 additions & 9 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
/target
/classes
/checkouts
target/
classes/
checkouts/
pom.xml
pom.xml.asc
*.jar
*.class
*.iml
/.lein-*
/.nrepl-port
/.project
/.classpath
/.settings
/.idea
.lein-*
.nrepl-port
.project
.classpath
.settings
.idea/
\#*\#
.\#*
.prepl-port
.clj-kondo/
.cpcache/
.lsp/
22 changes: 12 additions & 10 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
dist: precise
sudo: false
language: clojure
lein: lein2
script: lein2 midje
notifications:
irc: "irc.freenode.org#clj-postgresql"
jdk:
- oraclejdk8
#lein: lein
script:
- lein sub install
- lein sub test
#notifications:
# irc: "irc.freenode.org#clj-postgresql"
services:
- postgresql
addons:
postgresql: 9.4
postgresql: "10"
before_script:
- psql -U postgres -c 'CREATE DATABASE clj_pg_test;' postgres
- psql -U postgres -c 'CREATE EXTENSION postgis;' clj_pg_test
after_failure:
- psql -c '\d' -U postgres
- cat /var/log/postgresql/postgresql-9.4-main.log
- cat /var/log/postgresql/postgresql-10-main.log
env: PGDATABASE='clj_pg_test' PGUSER='postgres'
#virt: lxd
#os: linux
#dist: focal
#arch: arm64
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Add the following to the `:dependencies` section of your `project.clj` file:
[Leiningen](https://github.com/technomancy/leiningen) dependency information:

```clj
[clj-postgresql "0.7.0"]
[clj-postgresql "1.0.0-SNAPSHOT"]
```

[Maven](http://maven.apache.org/) dependency information:
Expand All @@ -26,7 +26,7 @@ Add the following to the `:dependencies` section of your `project.clj` file:
<dependency>
<groupId>clj-postgresql</groupId>
<artifactId>clj-postgresql</artifactId>
<version>0.7.0-SNAPSHOT</version>
<version>1.0.0-SNAPSHOT</version>
</dependency>
```

Expand Down
10 changes: 10 additions & 0 deletions clj-postgresql-aws/project.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
(defproject clj-postgresql/clj-postgresql-aws "1.0.1-SNAPSHOT"
:description "PostgreSQL helpers for Clojure projects."
:url "https://github.com/remodoy/clj-postgresql"
:license {:name "Two clause BSD license"
:url "http://github.com/remodoy/clj-postgresql/README.md"}
:scm {:dir ".."}
:dependencies [[org.clojure/clojure "1.12.0"]
[clj-postgresql/clj-postgresql-core "1.0.1-SNAPSHOT"]
[com.amazonaws/aws-java-sdk-rds "1.12.782"]]
:repl-options {:init-ns clj-postgresql.aws})
55 changes: 55 additions & 0 deletions clj-postgresql-aws/src/clj_postgresql/aws.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
(ns clj-postgresql.aws
(:require [clj-postgresql.core :as pg])
(:import (com.amazonaws.services.rds.auth RdsIamAuthTokenGenerator GetIamAuthTokenRequest)
(com.amazonaws.auth DefaultAWSCredentialsProviderChain)
(com.amazonaws.regions DefaultAwsRegionProviderChain)))

(defn rds-env-spec
"Make a db spec map from RDS_* variables in environment. Elastic Beanstalk uses these variables."
[]
(let [rds-hostname (System/getenv "RDS_HOSTNAME")
rds-port (System/getenv "RDS_PORT")
rds-db-name (System/getenv "RDS_DB_NAME")
rds-username (System/getenv "RDS_USERNAME")
rds-password (System/getenv "RDS_PASSWORD")]
(cond-> {}
rds-db-name (assoc :dbname rds-db-name)
rds-hostname (assoc :host rds-hostname)
rds-port (assoc :port rds-port)
rds-username (assoc :user rds-username)
rds-password (assoc :password rds-password))))

(defn rds-spec
"Make a db spec that uses the RDS_* and PG* environment variables to connect to the database."
([opts]
(let [rds-opts (rds-env-spec)
merged-opts (merge rds-opts opts)]
(pg/spec merged-opts)))
([]
(rds-spec {})))

(defn make-auth-token [{:keys [host port user] :as opts}]
#_(println "opts:" opts)
(let [credentials-provider (DefaultAWSCredentialsProviderChain.)
region ^String (.getRegion (DefaultAwsRegionProviderChain.))
token-generator (-> (RdsIamAuthTokenGenerator/builder)
(.credentials credentials-provider)
(.region region)
(.build))
token-request (-> (GetIamAuthTokenRequest/builder)
(.hostname host)
(.port (or (when port (Integer/parseInt port)) 5432))
(.userName user)
(.build))]
#_(println "region:" region)
(-> token-generator
(.getAuthToken token-request))))

(defn rds-iam-spec
"Make a db spec with IAM auth token (if no password given)."
([opts]
(let [spec-opts (rds-spec opts)]
(cond-> spec-opts
(not (contains? spec-opts :password)) (assoc :password (make-auth-token spec-opts)))))
([]
(rds-iam-spec {})))
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
(ns clj-postgresql.clj-postgresql-aws-test
(:require [clojure.test :refer :all]
[clj-postgresql.clj-postgresql-aws :refer :all]))

(deftest a-test
(testing "FIXME, I fail."
(is (= 0 1))))
11 changes: 11 additions & 0 deletions clj-postgresql-core/project.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
(defproject clj-postgresql/clj-postgresql-core "1.0.1-SNAPSHOT"
:description "PostgreSQL helpers for Clojure projects."
:url "https://github.com/remodoy/clj-postgresql"
:license {:name "Two clause BSD license"
:url "http://github.com/remodoy/clj-postgresql/README.md"}
:scm {:dir ".."}
:dependencies [[org.clojure/clojure "1.12.0"]
[org.postgresql/postgresql "42.7.5"]
[cheshire "5.13.0"]
[org.clojure/java.jdbc "0.7.11"]]
:repl-options {:init-ns clj-postgresql.core})
71 changes: 71 additions & 0 deletions clj-postgresql-core/src/clj_postgresql/core.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
(ns clj-postgresql.core
"Allow using PostgreSQL from Clojure as effortlessly as possible by reading connection parameter defaults from
PostgreSQL environment variables PGDATABASE, PGHOST, PGPORT, PGUSER and by reading password from ~/.pgpass if available."
(:require [clj-postgresql.pgpass :as pgpass]
[clojure.java.jdbc :as jdbc]))

(defn- getenv->map
"Convert crazy non-map thingy which comes from (System/getenv) into a keywordized map.
If no argument given, fetch env with (System/getenv)."
([x]
{:post [(map? %)]}
(zipmap
(map keyword (keys x))
(vals x)))
([]
(getenv->map (System/getenv))))

(defn default-spec
"Reasonable defaults as with the psql command line tool.
Use username for user and db. Don't use host."
[]
(let [username (java.lang.System/getProperty "user.name")]
{:dbtype "postgresql"
:user username
:dbname username}))

(defn env-spec
"Get db spec by reading PG* variables from the environment."
[{:keys [PGDATABASE PGHOST PGPORT PGUSER PGPASS] :as env}]
{:pre [(map? env)]
:post [(map? %)]}
(cond-> {}
PGDATABASE (assoc :dbname PGDATABASE)
PGHOST (assoc :host PGHOST)
PGPORT (assoc :port PGPORT)
PGUSER (assoc :user PGUSER)
PGPASS (assoc :password PGPASS)))

(defn spec
"Create database spec for PostgreSQL. Uses PG* environment variables by default
and acceps options in the form:
(spec {:dbname ... :host ... :port ... :user ... :password ...})"
([opts]
{:post [(contains? % :dbname)
(contains? % :user)]}
(let [default-spec-opts (default-spec)
env-spec-opts (env-spec (getenv->map (System/getenv)))
spec-opts (select-keys opts [:dbname :host :port :user :password])
extra-opts (dissoc opts :dbname :host :port :user :password)
db-spec (merge default-spec-opts env-spec-opts spec-opts)
password (when-not (:password db-spec)
(pgpass/pgpass-lookup db-spec))]
(cond-> (merge extra-opts db-spec)
password (assoc :password password))))
([]
(spec {})))

(defn close!
"Close db-spec if possible. Return true if the datasource was closeable and closed."
[{:keys [datasource]}]
(when (instance? java.io.Closeable datasource)
(.close ^java.io.Closeable datasource)
true))

(defn tables
[db]
(jdbc/with-db-metadata [md db]
(->> (doall (jdbc/metadata-result (.getTables md nil nil nil (into-array ["TABLE"]))))
(map :table_name)
(map keyword)
(set))))
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
Return a map of fields {:pg-hostname \"*\" ...}"
[s]
(zipmap
[:pg-hostname :pg-port :pg-database :pg-username :pg-password]
(str/split s #":")))
[:pg-hostname :pg-port :pg-database :pg-username :pg-password]
(str/split s #":")))

(defn read-pgpass
"Find ~/.pgpass, read it and parse lines into maps"
Expand All @@ -27,11 +27,11 @@
"(filter (partial pgpass-matches? spec) pgpass-lines)"
[{:keys [host port dbname user]} {:keys [pg-hostname pg-port pg-database pg-username pg-password]}]
(when
(and
(or (= pg-hostname "*") (= pg-hostname host) (and (= pg-hostname "localhost") (nil? host)))
(or (= pg-port "*") (= pg-port port) (and (= pg-port "5432") (nil? port)))
(or (= pg-database "*") (= pg-database dbname))
(or (= pg-username "*") (= pg-username user)))
(and
(or (= pg-hostname "*") (= pg-hostname host) (and (= pg-hostname "localhost") (nil? host)))
(or (= pg-port "*") (= pg-port port) (and (= pg-port "5432") (nil? port)))
(or (= pg-database "*") (= pg-database dbname))
(or (= pg-username "*") (= pg-username user)))
pg-password))

(defn pgpass-lookup
Expand Down
43 changes: 43 additions & 0 deletions clj-postgresql-core/src/clj_postgresql/types.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
(ns clj-postgresql.types
"Participate in clojure.java.jdbc's ISQLValue and IResultSetReadColumn protocols
to allow using PostGIS geometry types without the PGgeometry wrapper, support the
PGjson type and allow coercing clojure structures into PostGIS types.

clojure.java.jdbc/ISQLParameter protocol's set-parameter is used to set statement parameters.
Per default it just delegates to clojure.java.jdbc/ISQLValue protocol's sql-value method.
Thus if we have a special Clojure/Java type like org.postgis.Geometry, we can just implement
ISQLValue for that type. But if we want to convert generic maps, vectors, etc. to special database
types we need to implement ISQLParameter for the generic type and peek into statement's metadata
to figure out what the target type in database is.

For parameter handling we implement:
- map->parameter (IPersistentMap)
- vec->parameter (IPersistentVector, Sequable)
- num->parameter (Number)

Extend clojure.java.jdbc's protocol for converting query parameters to SQL values.
We try to determine which SQL type is correct for which clojure structure.
1. See query parameter meta data. JDBC might already know what PostgreSQL wants.
2. Look into parameter's clojure metadata for type hints
"
(:require [clj-postgresql.types maps vectors numbers json inet])
(:import (org.postgresql.util PGobject PGInterval PGmoney)))


(defn object
"Make a custom PGobject, e.g. (pg/object \"json\" \"{}\")"
[type value]
(doto (PGobject.)
(.setType (name type))
(.setValue (str value))))

(defn interval
"Create a PGinterval. (pg/interval :hours 2)"
[& {:keys [years months days hours minutes seconds]
:or {years 0 months 0 days 0 hours 0 minutes 0 seconds 0.0}}]
(PGInterval. years months days hours minutes ^double seconds))

(defn money
"Create PGmoney object"
[amount]
(PGmoney. ^double amount))
76 changes: 76 additions & 0 deletions clj-postgresql-core/src/clj_postgresql/types/geometric.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
(ns clj-postgresql.types.geometric
(:import (org.postgresql.geometric PGpoint PGbox PGcircle PGline PGlseg PGpath PGpolygon)))

;;
;; Constructors for geometric Types
;;

(defn point
"Create a PGpoint object"
([x y]
(PGpoint. x y))
([obj]
(cond
(instance? PGpoint obj) obj
(coll? obj) (point (first obj) (second obj))
:else (PGpoint. (str obj)))))

(defn box
"Create a PGbox object"
([p1 p2]
(PGbox. (point p1) (point p2)))
([x1 y1 x2 y2]
(PGbox. x1 y1 x2 y2))
([obj]
(if (instance? PGbox obj)
obj
(PGbox. (str obj)))))

(defn circle
"Create a PGcircle object"
([x y r]
(PGcircle. x y r))
([center-point r]
(PGcircle. (point center-point) r))
([obj]
(if (instance? PGcircle obj)
obj
(PGcircle. (str obj)))))

(defn line
"Create a PGline object"
([x1 y1 x2 y2]
(PGline. x1 y1 x2 y2))
([p1 p2]
(PGline. (point p1) (point p2)))
([obj]
(if (instance? PGline obj)
obj
(PGline. (str obj)))))

(defn lseg
"Create a PGlseg object"
([x1 y1 x2 y2]
(PGlseg. x1 y1 x2 y2))
([p1 p2]
(PGlseg. (point p1) (point p2)))
([obj]
(if (instance? PGlseg obj)
obj
(PGlseg. (str obj)))))

(defn path
"Create a PGpath object"
([points open?]
(PGpath. (into-array PGpoint (map point points)) open?))
([obj]
(if (instance? PGpath obj)
obj
(PGpath. (str obj)))))

(defn polygon
"Create a PGpolygon object"
[points-or-str]
(if (coll? points-or-str)
(PGpolygon. ^"[Lorg.postgresql.geometric.PGpoint;" (into-array PGpoint (map point points-or-str)))
(PGpolygon. ^String (str points-or-str))))
Loading