Skip to content

Commit

Permalink
Automatically create schema if needed
Browse files Browse the repository at this point in the history
  • Loading branch information
amacneil committed Nov 1, 2020
1 parent 90a7016 commit f909461
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 17 deletions.
26 changes: 26 additions & 0 deletions pkg/dbmate/postgres.go
Expand Up @@ -192,6 +192,32 @@ func (drv PostgresDriver) DatabaseExists(u *url.URL) (bool, error) {

// CreateMigrationsTable creates the schema_migrations table
func (drv PostgresDriver) CreateMigrationsTable(u *url.URL, db *sql.DB) error {
// get schema from URL search_path param
searchPath := strings.Split(u.Query().Get("search_path"), ",")
urlSchema := strings.TrimSpace(searchPath[0])
if urlSchema == "" {
urlSchema = "public"
}

// get *unquoted* current schema from database
dbSchema, err := queryRow(db, "select current_schema()")
if err != nil {
return err
}

// if urlSchema and dbSchema are not equal, the most likely explanation is that the schema
// has not yet been created
if urlSchema != dbSchema {
// in theory we could just execute this statement every time, but we do the comparison
// above in case the user doesn't have permissions to create schemas and the schema
// already exists
fmt.Printf("Creating schema: %s\n", urlSchema)
_, err = db.Exec("create schema if not exists " + pq.QuoteIdentifier(urlSchema))
if err != nil {
return err
}
}

migrationsTable, err := drv.migrationsTableName(db)
if err != nil {
return err
Expand Down
78 changes: 61 additions & 17 deletions pkg/dbmate/postgres_test.go
Expand Up @@ -119,7 +119,7 @@ func TestPostgresCreateDropDatabase(t *testing.T) {
defer mustClose(db)

err = db.Ping()
require.NotNil(t, err)
require.Error(t, err)
require.Equal(t, "pq: database \"dbmate\" does not exist", err.Error())
}()
}
Expand Down Expand Up @@ -191,32 +191,76 @@ func TestPostgresDatabaseExists_Error(t *testing.T) {
u.User = url.User("invalid")

exists, err := drv.DatabaseExists(u)
require.Error(t, err)
require.Equal(t, "pq: password authentication failed for user \"invalid\"", err.Error())
require.Equal(t, false, exists)
}

func TestPostgresCreateMigrationsTable(t *testing.T) {
drv := PostgresDriver{}
u := postgresTestURL(t)
db := prepTestPostgresDB(t, u)
defer mustClose(db)

// migrations table should not exist
count := 0
err := db.QueryRow("select count(*) from public.schema_migrations").Scan(&count)
require.Equal(t, "pq: relation \"public.schema_migrations\" does not exist", err.Error())
t.Run("default schema", func(t *testing.T) {
u := postgresTestURL(t)
db := prepTestPostgresDB(t, u)
defer mustClose(db)

// create table
err = drv.CreateMigrationsTable(u, db)
require.NoError(t, err)
// migrations table should not exist
count := 0
err := db.QueryRow("select count(*) from public.schema_migrations").Scan(&count)
require.Error(t, err)
require.Equal(t, "pq: relation \"public.schema_migrations\" does not exist", err.Error())

// migrations table should exist
err = db.QueryRow("select count(*) from public.schema_migrations").Scan(&count)
require.NoError(t, err)
// create table
err = drv.CreateMigrationsTable(u, db)
require.NoError(t, err)

// create table should be idempotent
err = drv.CreateMigrationsTable(u, db)
require.NoError(t, err)
// migrations table should exist
err = db.QueryRow("select count(*) from public.schema_migrations").Scan(&count)
require.NoError(t, err)

// create table should be idempotent
err = drv.CreateMigrationsTable(u, db)
require.NoError(t, err)
})

t.Run("custom schema", func(t *testing.T) {
u, err := url.Parse(postgresTestURL(t).String() + "&search_path=foo")
require.NoError(t, err)
db := prepTestPostgresDB(t, u)
defer mustClose(db)

// delete schema
_, err = db.Exec("drop schema if exists foo")
require.NoError(t, err)

// drop any schema_migrations table in public schema
_, err = db.Exec("drop table if exists public.schema_migrations")
require.NoError(t, err)

// migrations table should not exist in either schema
count := 0
err = db.QueryRow("select count(*) from foo.schema_migrations").Scan(&count)
require.Error(t, err)
require.Equal(t, "pq: relation \"foo.schema_migrations\" does not exist", err.Error())
err = db.QueryRow("select count(*) from public.schema_migrations").Scan(&count)
require.Error(t, err)
require.Equal(t, "pq: relation \"public.schema_migrations\" does not exist", err.Error())

// create table
err = drv.CreateMigrationsTable(u, db)
require.NoError(t, err)

// foo schema should be created, and migrations table should exist only in foo schema
err = db.QueryRow("select count(*) from foo.schema_migrations").Scan(&count)
require.NoError(t, err)
err = db.QueryRow("select count(*) from public.schema_migrations").Scan(&count)
require.Error(t, err)
require.Equal(t, "pq: relation \"public.schema_migrations\" does not exist", err.Error())

// create table should be idempotent
err = drv.CreateMigrationsTable(u, db)
require.NoError(t, err)
})
}

func TestPostgresSelectMigrations(t *testing.T) {
Expand Down

0 comments on commit f909461

Please sign in to comment.