-
Notifications
You must be signed in to change notification settings - Fork 0
/
pgmig.clj
66 lines (59 loc) · 2.55 KB
/
pgmig.clj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
(ns pgmig
(:require [clojure.java.io :as io]
[clojure.string :as string]
[next.jdbc :as jdbc]))
(defn- already-migrated [db]
(jdbc/execute! db ["CREATE TABLE IF NOT EXISTS pgmig_migrate (
id SERIAL PRIMARY KEY,
migration TEXT UNIQUE,
created TIMESTAMP NOT NULL DEFAULT current_timestamp
);"])
(jdbc/execute! db ["SELECT migration FROM pgmig_migrate ORDER BY id"]))
(defn- pending [done migrations]
(dorun (map (fn [{:keys [pgmig_migrate/migration]} {:keys [name]}]
(if-not (= name migration)
(throw (ex-info "unexpected migration"
{:expected name :actual migration}))))
done migrations))
(drop (count done) migrations))
(defn migrate
"Given a spec or data source, and a vector of maps containing :name and
:content, it will run the SQL content of all pending migrations if any.
Remember never to delete migrations, and only add new ones at the end."
[ds migrations]
(jdbc/with-transaction [tx ds]
(run! (fn [{:keys [name content]}]
(jdbc/execute! tx [content])
(jdbc/execute! tx ["INSERT INTO pgmig_migrate (migration) VALUES (?)" name]))
(pending (already-migrated tx) migrations))))
(defn- jar-seq [dir]
(let [[jurl prefix] (string/split (str dir) #"!/" 2)
jf (string/replace-first jurl #"^jar:file:" "")
jar (java.util.jar.JarFile. jf)
entries (.entries jar)]
(loop [result []]
(if (.hasMoreElements entries)
(let [name (.. entries nextElement getName)]
(recur (if (and (string/starts-with? name prefix)
(string/ends-with? name ".sql"))
(conj result {:name (subs name (count prefix))
:content (slurp (io/resource name))})
result)))
result))))
(defn- dir-seq [dir]
(into []
(comp
(filter (fn [^java.io.File f] (string/ends-with? (.getName f) ".sql")))
(map (fn [^java.io.File f] {:name (.getName f) :content (slurp f)})))
(-> dir io/as-file file-seq)))
(defn- protocol [dir]
(try
(-> dir io/as-url .getProtocol)
(catch java.net.MalformedURLException _e "")))
(defn migrations-from-dir
"Given a directory, loads all all .sql files and prepares them as a vector
of migrations as expected by the migrate function."
[dir]
(sort-by :name (if (= (protocol dir) "jar")
(jar-seq dir)
(dir-seq dir))))