Skip to content

Commit

Permalink
Add clj migrations
Browse files Browse the repository at this point in the history
  • Loading branch information
swlkr committed Feb 6, 2019
1 parent 3e7a05b commit 8e26647
Show file tree
Hide file tree
Showing 7 changed files with 455 additions and 146 deletions.
25 changes: 0 additions & 25 deletions resources/sql/migrations.sql

This file was deleted.

298 changes: 298 additions & 0 deletions src/coast/db/migrations.clj
@@ -0,0 +1,298 @@
(ns coast.db.migrations
(:require [coast.utils :as utils]
[coast.env :as env]
[clojure.edn :as edn]
[clojure.string :as string])
(:refer-clojure :exclude [boolean]))

(def rollback? (atom false))
(def vectors (atom []))

(def sql {"sqlite" {:timestamp "timestamp"
:now "current_timestamp"
:pk "integer primary key"}
"pg" {:timestamp "timestamptz"
:now "now()"
:pk "serial primary key"}})


(defn not-null [m]
(when (false? (:null m))
"not null"))


(defn col-default [m]
(when (contains? m :default)
(str "default " (get m :default))))


(defn unique [m]
(when (true? (:unique m))
(str "unique")))


(defn collate [m]
(when (contains? m :collate)
(str "collate " (get m :collate))))


(defn col-type [type {:keys [precision scale]}]
(if (or (some? precision)
(some? scale))
(str (or (utils/sqlize type) "numeric")
(when (or (some? precision)
(some? scale))
(str "(" (string/join ","
(filter some? [(or precision 0) scale]))
")")))
(utils/sqlize type)))


(defn on-delete [m]
(when (contains? m :on-delete)
(str "on delete " (utils/sqlize (:on-delete m)))))


(defn on-update [m]
(when (contains? m :on-update)
(str "on update " (utils/sqlize (:on-update m)))))


(defn reference [m]
(when (contains? m :references)
(str "references " (:references m))))


(defn col [type col-name m]
"SQL fragment for adding a column in create or alter table"
(swap! vectors conj [:col type col-name (or m {})])
(->> [(utils/sqlize col-name)
(col-type type m)
(unique m)
(collate m)
(not-null m)
(col-default m)
(reference m)
(on-delete m)
(on-update m)]
(filter some?)
(string/join " ")
(string/trim)))


(defn references [col-name & {:as m}]
(col :integer col-name (merge {:null false :references (str (utils/sqlize col-name) "(id)") :index true :on-delete "cascade"} m)))


(defn add-column
"SQL for adding a column to an existing table"
[table col-name type & {:as m}]
(str "alter table " (utils/sqlize table) " add column " (col type col-name m)))


(defn add-foreign-key
"SQL for adding a foreign key column to an existing table"
[from to & {col :col pk :pk fk-name :name :as m}]
(let [from (utils/sqlize from)
to (utils/sqlize to)]
(string/join " "
(filter some?
["alter table"
from
"add constraint"
(or (utils/sqlize fk-name) (str from "_" to "_fk"))
"foreign key"
(str "(" (or (utils/sqlize col) to) ")")
"references"
to
(str "(" (or (utils/sqlize pk) "id") ")")
(on-delete m)
(on-update m)]))))


(defn where [m]
(when (contains? m :where)
(str "where " (:where m))))


(defn index-cols [cols {order :order}]
(->> (map #(conj [%] (get order %)) cols)
(map #(map utils/sqlize %))
(map #(string/join " " %))
(map string/trim)))


(defn add-index
"SQL for adding an index to an existing table"
[table-name cols & {:as m}]
(let [table-name (utils/sqlize table-name)
cols (if (sequential? cols)
cols
[cols])
cols (index-cols cols m)
col-name (string/join ", " cols)
index-col-names (map #(string/replace % #" " "_") cols)
index-name (or (:name m) (str table-name "_" (string/join "_" index-col-names) "_index"))]
(string/join " "
(filter some?
["create"
(unique m)
"index"
index-name
"on"
table-name
(str "(" col-name ")")
(where m)]))))


(defn add-reference
"SQL for adding a foreign key column to an existing table"
[table-name ref-name & {:as m}]
(string/join " "
(filter some?
["alter table"
(utils/sqlize table-name)
"add column"
(utils/sqlize ref-name)
(or (-> m :type utils/sqlize) "integer")
"references"
(utils/sqlize ref-name)
(str "(id)")])))


(defn alter-column [table-name col-name type & {:as m}]
(string/join " "
(filter some?
["alter table"
(utils/sqlize table-name)
"alter column"
(utils/sqlize col-name)
"type"
(utils/sqlize type)
(when (contains? m :using)
(str "using " (:using m)))])))


(defn text [col-name & {:as m}]
(col :text col-name m))


(defn timestamp [col-name & {:as m}]
(col :timestamp col-name m))


(defn datetime [col-name & {:as m}]
(col :datetime col-name m))


(defn timestamptz [col-name & {:as m}]
(col :timestamptz col-name m))


(defn integer [col-name & {:as m}]
(col :integer col-name m))


(defn boolean [col-name & {:as m}]
(col :boolean col-name m))


(defn decimal [col-name & {:as m}]
(col :decimal col-name m))


(defn drop-table [table]
(str "drop table " (utils/sqlize table)))


(defn has-index? [v]
(true? (get (last v) :index)))


(defn create-table
"SQL to create a table"
[table & args]
(if (true? @rollback?)
(drop-table table)
(let [args (if (sequential? args) args '())
index-vectors (filter has-index? @vectors)
index-sql-strings (map #(add-index table (nth % 2)) index-vectors)]
(concat
[(string/join " "
(filter some?
[(str "create table " (utils/sqlize table) " (")
(string/join ", "
(conj args (str "id " (get-in sql [(env/env :db-adapter) :pk]))))
")"]))]
index-sql-strings))))


(defn create-extension [s]
(str "create extension " s))


(defn drop-extension [s]
(str "drop extension " s))


(defn drop-column
"SQL for dropping a column from a table"
[table col]
(str "alter table " (utils/sqlize table) " drop column " (utils/sqlize col)))


(defn drop-foreign-key [alter-table-name & {:as m}]
(let [constraint (when (contains? m :table)
(utils/sqlize (:table m)) "_" (utils/sqlize alter-table-name) "_fkey")
constraint (if (contains? m :name)
(utils/sqlize (:name m))
constraint)]
(str "alter table " (utils/sqlize alter-table-name) " drop constraint " constraint)))


(defn drop-index [table-name & {cols :column :as m}]
(let [cols (if (sequential? cols) cols [cols])
cols (index-cols cols m)
col-name (string/join ", " cols)
index-col-names (map #(string/replace % #" " "_") cols)
index-name (or (-> m :name utils/sqlize) (str table-name "_" (string/join "_" index-col-names) "_index"))]
(str "drop index " index-name)))


(defn drop-reference [table-name ref-name]
(str "alter table "
(utils/sqlize table-name)
" drop constraint "
(utils/sqlize ref-name) "_" (utils/sqlize table-name) "_fkey"))


(defn rename-column [table-name column-name new-column-name]
(string/join " "
["alter table"
(utils/sqlize table-name)
"rename column"
(utils/sqlize column-name)
"to"
(utils/sqlize new-column-name)]))


(defn rename-index [index-name new-index-name]
(string/join " "
["alter index"
index-name
"rename to"
new-index-name]))


(defn rename-table [table-name new-table-name]
(string/join " "
["alter table"
table-name
"rename to"
new-table-name]))


(defn timestamps []
(string/join " "
[(str "updated_at " (get-in sql [(env/env :db-adapter) :timestamp]) ",")
(str "created_at " (get-in sql [(env/env :db-adapter) :timestamp]) " not null default " (get-in sql [(env/env :db-adapter) :now]))]))
16 changes: 7 additions & 9 deletions src/coast/generators.clj
Expand Up @@ -2,8 +2,8 @@
(:require [clojure.string :as string]
[clojure.java.io :as io]
[coast.generators.action :as generators.action]
[coast.migrations.sql :as migrations.sql]
[coast.migrations.edn :as migrations.edn]))
[coast.generators.migration :as generators.migration]))


(defn usage []
(println "Usage:
Expand All @@ -13,22 +13,20 @@ Examples:
coast new foo
coast new another-foo
coast gen migration <name> # Creates a new edn migration file
coast gen sql:migration <name> # Creates a new sql migration file
coast gen migration <name> # Creates a new migration file
coast gen migration <name>.sql # Creates a new plain old sql migration file
coast gen action <resource> # Creates a five new clj files with view/action functions in src/<resource>/create/read/update/delete/list.clj
coast gen action <resource>:<action> # Creates a new clj file with view/action functions in src/<resource>/<action>.clj"))


(defn gen [args]
(let [[_ kind arg] args]
(case kind
"sql:migration" (migrations.sql/create arg)
"migration" (migrations.edn/create arg)
"migration" (generators.migration/write (drop 2 args))
"action" (generators.action/write arg)
"jobs" (->> (io/resource "migrations/create_jobs.sql")
(slurp)
(spit (str "resources/migrations/" (migrations.sql/filename "create-jobs"))))
(usage))))


(defn -main [& args]
(let [[action] args]
(case action
Expand Down

0 comments on commit 8e26647

Please sign in to comment.