Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
4e771345ea | |||
e26b6eb01c | |||
ad1540ca20 | |||
e35a5177ef | |||
9f38da0043 | |||
c933d6ac2b | |||
b49b648358 | |||
37c1c01ffc |
@ -1,14 +1,16 @@
|
|||||||
apply plugin: 'groovy'
|
apply plugin: 'groovy'
|
||||||
apply plugin: 'application'
|
apply plugin: 'application'
|
||||||
|
apply plugin: 'maven'
|
||||||
|
|
||||||
group = 'com.jdblabs'
|
group = 'com.jdblabs'
|
||||||
version = '0.2.2'
|
version = '0.2.5'
|
||||||
|
|
||||||
mainClassName = 'com.jdblabs.dbmigrate.DbMigrate'
|
mainClassName = 'com.jdblabs.dbmigrate.DbMigrate'
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenLocal()
|
mavenLocal()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
maven { url "http://mvn.jdb-labs.com/repo" }
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# Package
|
# Package
|
||||||
|
|
||||||
bin = @["db_migrate"]
|
bin = @["db_migrate"]
|
||||||
version = "0.2.2"
|
version = "0.2.7"
|
||||||
author = "Jonathan Bernard"
|
author = "Jonathan Bernard"
|
||||||
description = "Simple tool to handle database migrations."
|
description = "Simple tool to handle database migrations."
|
||||||
license = "BSD"
|
license = "BSD"
|
||||||
|
@ -15,7 +15,7 @@ import org.slf4j.LoggerFactory
|
|||||||
|
|
||||||
public class DbMigrate {
|
public class DbMigrate {
|
||||||
|
|
||||||
public static final VERSION = "0.2.2"
|
public static final VERSION = "0.2.5"
|
||||||
|
|
||||||
public static final def DOC = """\
|
public static final def DOC = """\
|
||||||
db-migrate.groovy v${VERSION}
|
db-migrate.groovy v${VERSION}
|
||||||
@ -47,7 +47,7 @@ Options:
|
|||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
|
||||||
// Parse arguments
|
// Parse arguments
|
||||||
def opts = new Docopt(DOC).withVersion("wdiwtlt v$VERSION").parse(args)
|
def opts = new Docopt(DOC).withVersion("db-migrate.groovy v$VERSION").parse(args)
|
||||||
|
|
||||||
if (opts['--version']) {
|
if (opts['--version']) {
|
||||||
println "db-migrate.groovy v$VERSION"
|
println "db-migrate.groovy v$VERSION"
|
||||||
@ -222,17 +222,7 @@ CREATE TABLE IF NOT EXISTS migrations (
|
|||||||
throw new FileNotFoundException(migrationFile.canonicalPath +
|
throw new FileNotFoundException(migrationFile.canonicalPath +
|
||||||
"does not exist or is not a regular file.")
|
"does not exist or is not a regular file.")
|
||||||
|
|
||||||
LOGGER.trace('Raw statements:\n\n{}\n', migrationFile.text.split(/;/).join('\n'))
|
runFile(migrationFile)
|
||||||
|
|
||||||
List<String> statements = migrationFile.text.split(/;/)
|
|
||||||
.collect { it.replaceAll(/--.*$/, '').trim() }
|
|
||||||
.findAll { it.length() > 0 }
|
|
||||||
|
|
||||||
LOGGER.trace('Statements:\n\n{}\n', statements.join('\n'))
|
|
||||||
|
|
||||||
statements.each {
|
|
||||||
LOGGER.trace('Executing SQL: {}', it)
|
|
||||||
sql.execute(it) }
|
|
||||||
|
|
||||||
if (up) sql.execute(
|
if (up) sql.execute(
|
||||||
'INSERT INTO migrations (name) VALUES (?)', migrationName)
|
'INSERT INTO migrations (name) VALUES (?)', migrationName)
|
||||||
@ -248,4 +238,17 @@ CREATE TABLE IF NOT EXISTS migrations (
|
|||||||
catch (Exception e) { sql.execute('ROLLBACK'); }
|
catch (Exception e) { sql.execute('ROLLBACK'); }
|
||||||
|
|
||||||
return migrationsRun }
|
return migrationsRun }
|
||||||
|
|
||||||
|
public void runFile(File file) {
|
||||||
|
LOGGER.trace('Raw statements:\n\n{}\n', file.text.split(/;/).join('\n'))
|
||||||
|
|
||||||
|
List<String> statements = file.text.split(/;/)
|
||||||
|
.collect { it.replaceAll(/--.*$/, '').trim() }
|
||||||
|
.findAll { it.length() > 0 }
|
||||||
|
|
||||||
|
LOGGER.trace('Statements:\n\n{}\n', statements.join('\n'))
|
||||||
|
|
||||||
|
statements.each {
|
||||||
|
LOGGER.trace('Executing SQL: {}', it)
|
||||||
|
sql.execute(it) } }
|
||||||
}
|
}
|
||||||
|
@ -9,12 +9,18 @@ import algorithm, json, times, os, strutils, docopt, db_postgres, sets,
|
|||||||
type
|
type
|
||||||
DbMigrateConfig* = tuple[ driver, sqlDir, connectionString: string, logLevel: Level ]
|
DbMigrateConfig* = tuple[ driver, sqlDir, connectionString: string, logLevel: Level ]
|
||||||
|
|
||||||
proc createMigrationsTable(conn: DbConn): void =
|
proc ensureMigrationsTableExists(conn: DbConn): void =
|
||||||
conn.exec(sql("""
|
let tableCount = conn.getValue(sql"""
|
||||||
|
SELECT COUNT(*) FROM information_schema.tables
|
||||||
|
WHERE table_name = 'migrations';""")
|
||||||
|
|
||||||
|
if tableCount.strip == "0":
|
||||||
|
info "Creating the migrations table as it does not already exist."
|
||||||
|
conn.exec(sql("""
|
||||||
CREATE TABLE IF NOT EXISTS migrations (
|
CREATE TABLE IF NOT EXISTS migrations (
|
||||||
id SERIAL PRIMARY KEY,
|
id SERIAL PRIMARY KEY,
|
||||||
name VARCHAR NOT NULL,
|
name VARCHAR NOT NULL,
|
||||||
run_at TIMESTAMP NOT NULL DEFAULT NOW())"""))
|
run_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW())"""))
|
||||||
|
|
||||||
proc rollbackWithErr (pgConn: DbConn, errMsg: string): void =
|
proc rollbackWithErr (pgConn: DbConn, errMsg: string): void =
|
||||||
pgConn.exec(sql"ROLLBACK")
|
pgConn.exec(sql"ROLLBACK")
|
||||||
@ -24,15 +30,27 @@ proc loadConfig*(filename: string): DbMigrateConfig =
|
|||||||
## Load DbMigrateConfig from a file.
|
## Load DbMigrateConfig from a file.
|
||||||
let cfg = json.parseFile(filename)
|
let cfg = json.parseFile(filename)
|
||||||
|
|
||||||
var logLevel: Level
|
var logLevel: Level = lvlInfo
|
||||||
if cfg.hasKey("logLevel"):
|
if existsEnv("DBM_LOG_LEVEL"):
|
||||||
|
let idx = find(LevelNames, $getEnv("DBM_LOG_LEVEL").toUpper)
|
||||||
|
logLevel = if idx == -1: lvlInfo else: (Level)(idx)
|
||||||
|
elif cfg.hasKey("logLevel"):
|
||||||
let idx = find(LevelNames, cfg["logLevel"].getStr.toUpper)
|
let idx = find(LevelNames, cfg["logLevel"].getStr.toUpper)
|
||||||
logLevel = if idx == -1: lvlInfo else: (Level)(idx)
|
logLevel = if idx == -1: lvlInfo else: (Level)(idx)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
driver: if cfg.hasKey("driver"): cfg["driver"].getStr else: "postres",
|
driver:
|
||||||
sqlDir: if cfg.hasKey("sqlDir"): cfg["sqlDir"].getStr else: "migrations",
|
if existsEnv("DATABASE_DRIVER"): $getEnv("DATABASE_DRIVER")
|
||||||
connectionString: cfg["connectionString"].getStr,
|
elif cfg.hasKey("driver"): cfg["driver"].getStr
|
||||||
|
else: "postres",
|
||||||
|
sqlDir:
|
||||||
|
if existsEnv("MIGRATIONS_DIR"): $getEnv("MIGRATIONS_DIR")
|
||||||
|
elif cfg.hasKey("sqlDir"): cfg["sqlDir"].getStr
|
||||||
|
else: "migrations",
|
||||||
|
connectionString:
|
||||||
|
if existsEnv("DATABASE_URL"): $getEnv("DATABASE_URL")
|
||||||
|
elif cfg.hasKey("connectionString"): cfg["connectionString"].getStr
|
||||||
|
else: "",
|
||||||
logLevel: logLevel)
|
logLevel: logLevel)
|
||||||
|
|
||||||
proc createMigration*(config: DbMigrateConfig, migrationName: string): seq[string] =
|
proc createMigration*(config: DbMigrateConfig, migrationName: string): seq[string] =
|
||||||
@ -96,10 +114,21 @@ proc diffMigrations*(pgConn: DbConn, config: DbMigrateConfig):
|
|||||||
missing: missingMigrations)
|
missing: missingMigrations)
|
||||||
|
|
||||||
proc readStatements*(filename: string): seq[SqlQuery] =
|
proc readStatements*(filename: string): seq[SqlQuery] =
|
||||||
let migrationSql = filename.readFile
|
result = @[]
|
||||||
result = migrationSql.split(';').
|
var stmt: string = ""
|
||||||
filter(proc(st: string): bool = st.strip.len > 0 and not st.startsWith("--")).
|
|
||||||
map(proc(st: string): SqlQuery = sql(st & ";"))
|
for line in filename.lines:
|
||||||
|
let l = line.strip
|
||||||
|
if l.len == 0 or l.startsWith("--"): continue
|
||||||
|
|
||||||
|
let parts = line.split(';')
|
||||||
|
stmt &= "\n" & parts[0]
|
||||||
|
|
||||||
|
if parts.len > 1:
|
||||||
|
result.add(sql(stmt & ";"))
|
||||||
|
stmt = parts[1] & "";
|
||||||
|
|
||||||
|
if stmt.strip.len > 0: result.add(sql(stmt))
|
||||||
|
|
||||||
proc up*(pgConn: DbConn, config: DbMigrateConfig, toRun: seq[string]): seq[string] =
|
proc up*(pgConn: DbConn, config: DbMigrateConfig, toRun: seq[string]): seq[string] =
|
||||||
var migrationsRun = newSeq[string]()
|
var migrationsRun = newSeq[string]()
|
||||||
@ -118,7 +147,8 @@ proc up*(pgConn: DbConn, config: DbMigrateConfig, toRun: seq[string]): seq[strin
|
|||||||
let statements = filename.readStatements
|
let statements = filename.readStatements
|
||||||
|
|
||||||
try:
|
try:
|
||||||
for statement in statements: pgConn.exec(statement)
|
for statement in statements:
|
||||||
|
pgConn.exec(statement)
|
||||||
pgConn.exec(sql"INSERT INTO migrations (name) VALUES (?);", migration)
|
pgConn.exec(sql"INSERT INTO migrations (name) VALUES (?);", migration)
|
||||||
except DbError:
|
except DbError:
|
||||||
pgConn.rollbackWithErr "Migration '" & migration & "' failed:\n\t" &
|
pgConn.rollbackWithErr "Migration '" & migration & "' failed:\n\t" &
|
||||||
@ -182,7 +212,7 @@ Options:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# Parse arguments
|
# Parse arguments
|
||||||
let args = docopt(doc, version = "db-migrate 0.2.2")
|
let args = docopt(doc, version = "db-migrate (Nim) 0.2.7\nhttps://git.jdb-labs.com/jdb/db-migrate")
|
||||||
|
|
||||||
let exitErr = proc(msg: string): void =
|
let exitErr = proc(msg: string): void =
|
||||||
fatal("db_migrate: " & msg)
|
fatal("db_migrate: " & msg)
|
||||||
@ -190,16 +220,17 @@ Options:
|
|||||||
|
|
||||||
# Load configuration file
|
# Load configuration file
|
||||||
let configFilename =
|
let configFilename =
|
||||||
if args["--config"]: $args["<config-file>"]
|
if args["--config"]: $args["--config"]
|
||||||
else: "database.json"
|
else: "database.json"
|
||||||
|
|
||||||
var config: DbMigrateConfig
|
var config: DbMigrateConfig
|
||||||
try:
|
try:
|
||||||
config = loadConfig(configFilename)
|
config = loadConfig(configFilename)
|
||||||
except IOError:
|
except IOError:
|
||||||
exitErr "Cannot open config file: " & configFilename
|
writeLine(stderr, "db_migrate: Cannot open config file: " & configFilename)
|
||||||
except:
|
except:
|
||||||
exitErr "Error parsing config file: " & configFilename & "\L\t" & getCurrentExceptionMsg()
|
writeLine(stderr, "db_migrate: Error parsing config file: " &
|
||||||
|
configFilename & "\L\t" & getCurrentExceptionMsg())
|
||||||
|
|
||||||
|
|
||||||
logging.addHandler(newConsoleLogger())
|
logging.addHandler(newConsoleLogger())
|
||||||
@ -233,7 +264,7 @@ Options:
|
|||||||
exitErr "Unable to connect to the database: " & getCurrentExceptionMsg()
|
exitErr "Unable to connect to the database: " & getCurrentExceptionMsg()
|
||||||
(DbConn)(nil)
|
(DbConn)(nil)
|
||||||
|
|
||||||
pgConn.createMigrationsTable
|
pgConn.ensureMigrationsTableExists
|
||||||
|
|
||||||
let (run, notRun, missing) = diffMigrations(pgConn, config)
|
let (run, notRun, missing) = diffMigrations(pgConn, config)
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user