Require presence of up block; optional space after -- (#72)

This commit is contained in:
Ben Reinhart 2019-05-20 16:37:20 -07:00 committed by GitHub
parent a31e06b438
commit 02c7bda8a9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 55 additions and 26 deletions

View file

@ -218,10 +218,9 @@ dbmate supports options passed to a migration block in the form of `key:value` p
```sql ```sql
-- migrate:up transaction:false -- migrate:up transaction:false
ALTER TYPE colors ADD VALUE 'orange' AFTER 'red'; ALTER TYPE colors ADD VALUE 'orange' AFTER 'red';
ALTER TYPE colors ADD VALUE 'yellow' AFTER 'orange';
``` ```
`transaction` will default to `true` for if your database supports it. `transaction` will default to `true` if your database supports it.
### Schema File ### Schema File

View file

@ -1,6 +1,7 @@
package dbmate package dbmate
import ( import (
"fmt"
"io/ioutil" "io/ioutil"
"regexp" "regexp"
"strings" "strings"
@ -36,37 +37,33 @@ func parseMigration(path string) (Migration, Migration, error) {
if err != nil { if err != nil {
return NewMigration(), NewMigration(), err return NewMigration(), NewMigration(), err
} }
up, down := parseMigrationContents(string(data)) up, down, err := parseMigrationContents(string(data))
return up, down, nil return up, down, err
} }
var upRegExp = regexp.MustCompile(`(?m)^-- migrate:up\s+(.+)*$`) var upRegExp = regexp.MustCompile(`(?m)^--\s*migrate:up\s+(.+)*$`)
var downRegExp = regexp.MustCompile(`(?m)^-- migrate:down\s+(.+)*$`) var downRegExp = regexp.MustCompile(`(?m)^--\s*migrate:down\s+(.+)*$`)
// parseMigrationContents parses the string contents of a migration. // parseMigrationContents parses the string contents of a migration.
// It will return two Migration objects, the first representing the "up" // It will return two Migration objects, the first representing the "up"
// block and the second representing the "down" block. // block and the second representing the "down" block. This function
// // requires that at least an up block was defined and will otherwise
// Note that with the way this is currently defined, it is possible to // return an error.
// correctly parse a migration that does not define an "up" block or a func parseMigrationContents(contents string) (Migration, Migration, error) {
// "down" block, or one that defines neither. This behavior is, in part,
// to preserve backwards compatibility.
func parseMigrationContents(contents string) (Migration, Migration) {
up := NewMigration() up := NewMigration()
down := NewMigration() down := NewMigration()
upMatch := upRegExp.FindStringSubmatchIndex(contents) upMatch := upRegExp.FindStringSubmatchIndex(contents)
downMatch := downRegExp.FindStringSubmatchIndex(contents) downMatch := downRegExp.FindStringSubmatchIndex(contents)
didNotDefineUpBlock := len(upMatch) == 0
onlyDefinedUpBlock := len(upMatch) != 0 && len(downMatch) == 0 onlyDefinedUpBlock := len(upMatch) != 0 && len(downMatch) == 0
onlyDefinedDownBlock := len(upMatch) == 0 && len(downMatch) != 0
if onlyDefinedUpBlock { if didNotDefineUpBlock {
return up, down, fmt.Errorf("dbmate requires each migration to define an up bock with '-- migrate:up'")
} else if onlyDefinedUpBlock {
up.Contents = strings.TrimSpace(contents) up.Contents = strings.TrimSpace(contents)
up.Options = parseMigrationOptions(contents, upMatch[2], upMatch[3]) up.Options = parseMigrationOptions(contents, upMatch[2], upMatch[3])
} else if onlyDefinedDownBlock {
down.Contents = strings.TrimSpace(contents)
down.Options = parseMigrationOptions(contents, downMatch[2], downMatch[3])
} else { } else {
upStart := upMatch[0] upStart := upMatch[0]
downStart := downMatch[0] downStart := downMatch[0]
@ -87,7 +84,7 @@ func parseMigrationContents(contents string) (Migration, Migration) {
down.Options = parseMigrationOptions(contents, downMatch[2], downMatch[3]) down.Options = parseMigrationOptions(contents, downMatch[2], downMatch[3])
} }
return up, down return up, down, nil
} }
var whitespaceRegExp = regexp.MustCompile(`\s+`) var whitespaceRegExp = regexp.MustCompile(`\s+`)

View file

@ -7,12 +7,14 @@ import (
) )
func TestParseMigrationContents(t *testing.T) { func TestParseMigrationContents(t *testing.T) {
// It supports the typical use case.
migration := `-- migrate:up migration := `-- migrate:up
create table users (id serial, name text); create table users (id serial, name text);
-- migrate:down -- migrate:down
drop table users;` drop table users;`
up, down := parseMigrationContents(migration) up, down, err := parseMigrationContents(migration)
require.Nil(t, err)
require.Equal(t, "-- migrate:up\ncreate table users (id serial, name text);", up.Contents) require.Equal(t, "-- migrate:up\ncreate table users (id serial, name text);", up.Contents)
require.Equal(t, true, up.Options.Transaction()) require.Equal(t, true, up.Options.Transaction())
@ -20,13 +22,33 @@ drop table users;`
require.Equal(t, "-- migrate:down\ndrop table users;", down.Contents) require.Equal(t, "-- migrate:down\ndrop table users;", down.Contents)
require.Equal(t, true, down.Options.Transaction()) require.Equal(t, true, down.Options.Transaction())
// It does not require space between the '--' and 'migrate'
migration = `
--migrate:up
create table users (id serial, name text);
--migrate:down
drop table users;
`
up, down, err = parseMigrationContents(migration)
require.Nil(t, err)
require.Equal(t, "--migrate:up\ncreate table users (id serial, name text);", up.Contents)
require.Equal(t, true, up.Options.Transaction())
require.Equal(t, "--migrate:down\ndrop table users;", down.Contents)
require.Equal(t, true, down.Options.Transaction())
// It is acceptable for down to be defined before up
migration = `-- migrate:down migration = `-- migrate:down
drop table users; drop table users;
-- migrate:up -- migrate:up
create table users (id serial, name text); create table users (id serial, name text);
` `
up, down = parseMigrationContents(migration) up, down, err = parseMigrationContents(migration)
require.Nil(t, err)
require.Equal(t, "-- migrate:up\ncreate table users (id serial, name text);", up.Contents) require.Equal(t, "-- migrate:up\ncreate table users (id serial, name text);", up.Contents)
require.Equal(t, true, up.Options.Transaction()) require.Equal(t, true, up.Options.Transaction())
@ -34,19 +56,30 @@ create table users (id serial, name text);
require.Equal(t, "-- migrate:down\ndrop table users;", down.Contents) require.Equal(t, "-- migrate:down\ndrop table users;", down.Contents)
require.Equal(t, true, down.Options.Transaction()) require.Equal(t, true, down.Options.Transaction())
// This migration would not work in Postgres if it were to // It supports turning transactions off for a given migration block,
// run in a transaction, so we would want to disable transactions. // e.g., the below would not work in Postgres inside a transaction.
// It also supports omitting the down block.
migration = `-- migrate:up transaction:false migration = `-- migrate:up transaction:false
ALTER TYPE colors ADD VALUE 'orange' AFTER 'red'; ALTER TYPE colors ADD VALUE 'orange' AFTER 'red';
ALTER TYPE colors ADD VALUE 'yellow' AFTER 'orange';
` `
up, down = parseMigrationContents(migration) up, down, err = parseMigrationContents(migration)
require.Nil(t, err)
require.Equal(t, "-- migrate:up transaction:false\nALTER TYPE colors ADD VALUE 'orange' AFTER 'red';\nALTER TYPE colors ADD VALUE 'yellow' AFTER 'orange';", up.Contents) require.Equal(t, "-- migrate:up transaction:false\nALTER TYPE colors ADD VALUE 'orange' AFTER 'red';", up.Contents)
require.Equal(t, false, up.Options.Transaction()) require.Equal(t, false, up.Options.Transaction())
require.Equal(t, "", down.Contents) require.Equal(t, "", down.Contents)
require.Equal(t, true, down.Options.Transaction()) require.Equal(t, true, down.Options.Transaction())
// It does *not* support omitting the up block.
migration = `-- drop users table
begin;
drop table users;
commit;
`
_, _, err = parseMigrationContents(migration)
require.NotNil(t, err)
require.Equal(t, "dbmate requires each migration to define an up bock with '-- migrate:up'", err.Error())
} }