diff --git a/sql_parser.go b/sql_parser.go index 91bc84d..d314a6e 100644 --- a/sql_parser.go +++ b/sql_parser.go @@ -29,6 +29,7 @@ type stateMachine parserState func (s *stateMachine) Get() parserState { return parserState(*s) } + func (s *stateMachine) Set(new parserState) { verboseInfo("StateMachine: %v => %v", *s, new) *s = stateMachine(new) @@ -65,6 +66,7 @@ func parseSQLMigration(r io.Reader, direction bool) (stmts []string, useTx bool, stateMachine := stateMachine(start) useTx = true + firstLineFound := false for scanner.Scan() { line := scanner.Text() if verbose { @@ -76,6 +78,7 @@ func parseSQLMigration(r io.Reader, direction bool) (stmts []string, useTx bool, switch cmd { case "+goose Up": + firstLineFound = true switch stateMachine.Get() { case start: stateMachine.Set(gooseUp) @@ -85,6 +88,7 @@ func parseSQLMigration(r io.Reader, direction bool) (stmts []string, useTx bool, continue case "+goose Down": + firstLineFound = true switch stateMachine.Get() { case gooseUp, gooseStatementEndUp: stateMachine.Set(gooseDown) @@ -94,6 +98,7 @@ func parseSQLMigration(r io.Reader, direction bool) (stmts []string, useTx bool, continue case "+goose StatementBegin": + firstLineFound = true switch stateMachine.Get() { case gooseUp, gooseStatementEndUp: stateMachine.Set(gooseStatementBeginUp) @@ -105,6 +110,7 @@ func parseSQLMigration(r io.Reader, direction bool) (stmts []string, useTx bool, continue case "+goose StatementEnd": + firstLineFound = true switch stateMachine.Get() { case gooseStatementBeginUp: stateMachine.Set(gooseStatementEndUp) @@ -125,8 +131,8 @@ func parseSQLMigration(r io.Reader, direction bool) (stmts []string, useTx bool, } } - // Ignore empty lines. - if matchEmptyLines.MatchString(line) { + // Ignore empty lines until first line is found. + if !firstLineFound && matchEmptyLines.MatchString(line) { verboseInfo("StateMachine: ignore empty line") continue } diff --git a/sql_parser_test.go b/sql_parser_test.go index 6420ebb..425dc13 100644 --- a/sql_parser_test.go +++ b/sql_parser_test.go @@ -74,6 +74,25 @@ func TestSplitStatements(t *testing.T) { } } +func TestKeepEmptyLines(t *testing.T) { + stmts, _, err := parseSQLMigration(strings.NewReader(emptyLineSQL), true) + if err != nil { + t.Errorf("Failed to parse SQL migration. %v", err) + } + expected := `INSERT INTO post (id, title, body) +VALUES ('id_01', 'my_title', ' +this is an insert statement including empty lines. + +empty (blank) lines can be meaningful. + +leave the lines to keep the text syntax. +'); +` + if stmts[0] != expected { + t.Errorf("incorrect stmts. got %v, want %v", stmts, expected) + } +} + func TestUseTransactions(t *testing.T) { t.Parallel() @@ -138,6 +157,20 @@ SELECT 4; -- 4th stmt DROP TABLE post; -- 1st stmt ` +var emptyLineSQL = `-- +goose Up +INSERT INTO post (id, title, body) +VALUES ('id_01', 'my_title', ' +this is an insert statement including empty lines. + +empty (blank) lines can be meaningful. + +leave the lines to keep the text syntax. +'); + +-- +goose Down +TRUNCATE TABLE post; +` + var functxt = `-- +goose Up CREATE TABLE IF NOT EXISTS histories ( id BIGSERIAL PRIMARY KEY,