Add Up command to create database and migrate

This commit is contained in:
Adrian Macneil 2015-11-27 10:34:42 -08:00
parent 1c4cf2c122
commit 164ec81370
5 changed files with 125 additions and 31 deletions

View file

@ -14,6 +14,32 @@ import (
"time"
)
// UpCommand creates the database (if necessary) and runs migrations
func UpCommand(ctx *cli.Context) error {
u, err := GetDatabaseURL(ctx)
if err != nil {
return err
}
drv, err := driver.Get(u.Scheme)
if err != nil {
return err
}
// create database if it does not already exist
// skip this step if we cannot determine status
// (e.g. user does not have list database permission)
exists, err := drv.DatabaseExists(u)
if err == nil && !exists {
if err := drv.CreateDatabase(u); err != nil {
return err
}
}
// migrate
return MigrateCommand(ctx)
}
// CreateCommand creates the current database
func CreateCommand(ctx *cli.Context) error {
u, err := GetDatabaseURL(ctx)

View file

@ -11,6 +11,7 @@ import (
// Driver provides top level database functions
type Driver interface {
Open(*url.URL) (*sql.DB, error)
DatabaseExists(*url.URL) (bool, error)
CreateDatabase(*url.URL) error
DropDatabase(*url.URL) error
CreateMigrationsTable(*sql.DB) error

View file

@ -18,39 +18,66 @@ func (postgres Driver) Open(u *url.URL) (*sql.DB, error) {
}
// postgresExec runs a sql statement on the "postgres" database
func (postgres Driver) postgresExec(u *url.URL, statement string) error {
func (postgres Driver) openPostgresDB(u *url.URL) (*sql.DB, error) {
// connect to postgres database
postgresURL := *u
postgresURL.Path = "postgres"
db, err := postgres.Open(&postgresURL)
return postgres.Open(&postgresURL)
}
// CreateDatabase creates the specified database
func (postgres Driver) CreateDatabase(u *url.URL) error {
name := shared.DatabaseName(u)
fmt.Printf("Creating: %s\n", name)
db, err := postgres.openPostgresDB(u)
if err != nil {
return err
}
defer db.Close()
// run statement
_, err = db.Exec(statement)
_, err = db.Exec(fmt.Sprintf("CREATE DATABASE %s",
pq.QuoteIdentifier(name)))
return err
}
// CreateDatabase creates the specified database
func (postgres Driver) CreateDatabase(u *url.URL) error {
database := shared.DatabaseName(u)
fmt.Printf("Creating: %s\n", database)
return postgres.postgresExec(u, fmt.Sprintf("CREATE DATABASE %s",
pq.QuoteIdentifier(database)))
}
// DropDatabase drops the specified database (if it exists)
func (postgres Driver) DropDatabase(u *url.URL) error {
database := shared.DatabaseName(u)
fmt.Printf("Dropping: %s\n", database)
name := shared.DatabaseName(u)
fmt.Printf("Dropping: %s\n", name)
return postgres.postgresExec(u, fmt.Sprintf("DROP DATABASE IF EXISTS %s",
pq.QuoteIdentifier(database)))
db, err := postgres.openPostgresDB(u)
if err != nil {
return err
}
defer db.Close()
_, err = db.Exec(fmt.Sprintf("DROP DATABASE IF EXISTS %s",
pq.QuoteIdentifier(name)))
return err
}
// DatabaseExists determines whether the database exists
func (postgres Driver) DatabaseExists(u *url.URL) (bool, error) {
name := shared.DatabaseName(u)
db, err := postgres.openPostgresDB(u)
if err != nil {
return false, err
}
defer db.Close()
exists := false
err = db.QueryRow("SELECT true FROM pg_database WHERE datname = $1", name).
Scan(&exists)
if err == sql.ErrNoRows {
return false, nil
}
return exists, err
}
// HasMigrationsTable returns true if the schema_migrations table exists

View file

@ -61,3 +61,36 @@ func TestCreateDropDatabase(t *testing.T) {
require.Equal(t, "pq: database \"dbmate\" does not exist", err.Error())
}()
}
func TestDatabaseExists(t *testing.T) {
d := postgres.Driver{}
u := testURL(t)
// drop any existing database
err := d.DropDatabase(u)
require.Nil(t, err)
// DatabaseExists should return false
exists, err := d.DatabaseExists(u)
require.Nil(t, err)
require.Equal(t, false, exists)
// create database
err = d.CreateDatabase(u)
require.Nil(t, err)
// DatabaseExists should return true
exists, err = d.DatabaseExists(u)
require.Nil(t, err)
require.Equal(t, true, exists)
}
func TestDatabaseExists_error(t *testing.T) {
d := postgres.Driver{}
u := testURL(t)
u.User = url.User("invalid")
exists, err := d.DatabaseExists(u)
require.Equal(t, "pq: role \"invalid\" does not exist", err.Error())
require.Equal(t, false, exists)
}

35
main.go
View file

@ -35,20 +35,6 @@ func NewApp() *cli.App {
}
app.Commands = []cli.Command{
{
Name: "migrate",
Usage: "Migrate to the latest version",
Action: func(ctx *cli.Context) {
runCommand(MigrateCommand, ctx)
},
},
{
Name: "rollback",
Usage: "Rollback the most recent migration",
Action: func(ctx *cli.Context) {
runCommand(RollbackCommand, ctx)
},
},
{
Name: "new",
Usage: "Generate a new migration file",
@ -56,6 +42,13 @@ func NewApp() *cli.App {
runCommand(NewCommand, ctx)
},
},
{
Name: "up",
Usage: "Create database (if necessary) and migrate to the latest version",
Action: func(ctx *cli.Context) {
runCommand(UpCommand, ctx)
},
},
{
Name: "create",
Usage: "Create database",
@ -70,6 +63,20 @@ func NewApp() *cli.App {
runCommand(DropCommand, ctx)
},
},
{
Name: "migrate",
Usage: "Migrate to the latest version",
Action: func(ctx *cli.Context) {
runCommand(MigrateCommand, ctx)
},
},
{
Name: "rollback",
Usage: "Rollback the most recent migration",
Action: func(ctx *cli.Context) {
runCommand(RollbackCommand, ctx)
},
},
}
return app