Skip to content

Commit

Permalink
Add godoc, increase coverage, move changeset
Browse files Browse the repository at this point in the history
  • Loading branch information
josephspurrier committed Jan 11, 2019
1 parent 37a2cca commit 7f88bc5
Show file tree
Hide file tree
Showing 10 changed files with 97 additions and 84 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
[![Go Report Card](https://goreportcard.com/badge/github.com/josephspurrier/rove)](https://goreportcard.com/report/github.com/josephspurrier/rove)
[![Build Status](https://travis-ci.org/josephspurrier/rove.svg)](https://travis-ci.org/josephspurrier/rove)
[![Coverage Status](https://coveralls.io/repos/github/josephspurrier/rove/badge.svg?branch=master&timestamp=20180923-01)](https://coveralls.io/github/josephspurrier/rove?branch=master)
[![GoDoc](https://godoc.org/github.com/josephspurrier/rove?status.svg)](https://godoc.org/github.com/josephspurrier/rove)

## MySQL Database Migration Tool Based on Liquibase

Expand Down
10 changes: 9 additions & 1 deletion interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,20 @@ type Migration interface {
Insert(id, author, filename string, count int, checksum, description, version string) error
// Returns a list of the changesets or it returns an error if there is an
// problem running the query.
Changesets(reverse bool) ([]DBChangeset, error)
Changesets(reverse bool) ([]Change, error)
// Delete the changeset from the database or return an error if there is a
// problem running the query.
Delete(id, author, filename string) error
}

// Change contains a single database record change.
type Change struct {
ID string
Author string
Filename string
OrderExecuted int
}

// Transaction represents a database transaction.
type Transaction interface {
// Commit will attempt to commit the changes to the database or return
Expand Down
24 changes: 13 additions & 11 deletions migrate.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"database/sql"
"fmt"
"strings"

"github.com/josephspurrier/rove/pkg/changeset"
)

// Migrate will perform all the migrations in a file. If max is 0, all
Expand All @@ -15,7 +17,7 @@ func (r *Rove) Migrate(max int) error {
return err
}

arr := make([]Changeset, 0)
arr := make([]changeset.Info, 0)
// If a file is specified, use it to build the array.
if len(r.file) > 0 {
// Get the changesets.
Expand All @@ -40,20 +42,20 @@ func (r *Rove) Migrate(max int) error {

// Determine if the changeset was already applied.
// Count the number of rows.
checksum, err = r.db.ChangesetApplied(cs.id, cs.author, cs.filename)
checksum, err = r.db.ChangesetApplied(cs.ID, cs.Author, cs.Filename)
if err == nil {
// Determine if the checksums match.
if checksum != newChecksum {
return fmt.Errorf("checksum does not match - existing changeset %v:%v has checksum %v, but new changeset has checksum %v",
cs.author, cs.id, checksum, newChecksum)
cs.Author, cs.ID, checksum, newChecksum)
}

if r.Verbose {
fmt.Printf("Changeset already applied: %v:%v\n", cs.author, cs.id)
fmt.Printf("Changeset already applied: %v:%v\n", cs.Author, cs.ID)
}
continue
} else if err != nil && err != sql.ErrNoRows {
return fmt.Errorf("internal error on changeset %v:%v - %v", cs.author, cs.id, err.Error())
return fmt.Errorf("internal error on changeset %v:%v - %v", cs.Author, cs.ID, err.Error())
}

arrQueries := strings.Split(cs.Changes(), ";")
Expand All @@ -72,17 +74,17 @@ func (r *Rove) Migrate(max int) error {
// Execute the query.
err = tx.Exec(q)
if err != nil {
return fmt.Errorf("sql error on changeset %v:%v - %v", cs.author, cs.id, err.Error())
return fmt.Errorf("sql error on changeset %v:%v - %v", cs.Author, cs.ID, err.Error())
}
}

err = tx.Commit()
if err != nil {
errr := tx.Rollback()
if errr != nil {
return fmt.Errorf("sql error on commit rollback %v:%v - %v", cs.author, cs.id, errr.Error())
return fmt.Errorf("sql error on commit rollback %v:%v - %v", cs.Author, cs.ID, errr.Error())
}
return fmt.Errorf("sql error on commit %v:%v - %v", cs.author, cs.id, err.Error())
return fmt.Errorf("sql error on commit %v:%v - %v", cs.Author, cs.ID, err.Error())
}

// Count the number of rows.
Expand All @@ -92,14 +94,14 @@ func (r *Rove) Migrate(max int) error {
}

// Insert the record.
err = r.db.Insert(cs.id, cs.author, cs.filename, count+1, newChecksum,
cs.description, cs.version)
err = r.db.Insert(cs.ID, cs.Author, cs.Filename, count+1, newChecksum,
cs.Description, cs.Version)
if err != nil {
return err
}

if r.Verbose {
fmt.Printf("Changeset applied: %v:%v\n", cs.author, cs.id)
fmt.Printf("Changeset applied: %v:%v\n", cs.Author, cs.ID)
}

// Only perform the maxium number of changes based on the max value.
Expand Down
43 changes: 34 additions & 9 deletions parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ import (
"path"
"path/filepath"
"strings"

"github.com/josephspurrier/rove/pkg/changeset"
)

// parseFileToArray will parse a file into changesets.
func parseFileToArray(filename string) ([]Changeset, error) {
func parseFileToArray(filename string) ([]changeset.Info, error) {
f, err := os.Open(filename)
if err != nil {
return nil, err
Expand All @@ -23,12 +25,12 @@ func parseFileToArray(filename string) ([]Changeset, error) {
}

// parseToArray will split the SQL migration into an ordered array.
func parseToArray(r io.Reader, filename string) ([]Changeset, error) {
func parseToArray(r io.Reader, filename string) ([]changeset.Info, error) {
scanner := bufio.NewScanner(r)
scanner.Split(bufio.ScanLines)

// Array of changesets.
arr := make([]Changeset, 0)
arr := make([]changeset.Info, 0)

for scanner.Scan() {
// Get the line without leading or trailing spaces.
Expand All @@ -55,7 +57,7 @@ func parseToArray(r io.Reader, filename string) ([]Changeset, error) {
// Start recording the changeset.
if strings.HasPrefix(line, elementChangeset) {
// Create a new changeset.
cs := new(Changeset)
cs := new(changeset.Info)
cs.ParseHeader(strings.TrimPrefix(line, elementChangeset))
cs.SetFileInfo(path.Base(filename), "sql", appVersion)
arr = append(arr, *cs)
Expand Down Expand Up @@ -86,7 +88,7 @@ func parseToArray(r io.Reader, filename string) ([]Changeset, error) {
}

// parseFileToMap will parse a file into a map.
func parseFileToMap(filename string) (map[string]Changeset, error) {
func parseFileToMap(filename string) (map[string]changeset.Info, error) {
f, err := os.Open(filename)
if err != nil {
return nil, err
Expand All @@ -97,7 +99,7 @@ func parseFileToMap(filename string) (map[string]Changeset, error) {
}

// parseReaderToMap will parse a reader to a map.
func parseReaderToMap(r io.Reader, filename string) (map[string]Changeset, error) {
func parseReaderToMap(r io.Reader, filename string) (map[string]changeset.Info, error) {
arr, err := parseToArray(r, filename)
if err != nil {
return nil, err
Expand All @@ -106,11 +108,11 @@ func parseReaderToMap(r io.Reader, filename string) (map[string]Changeset, error
return parseArrayToMap(arr)
}

func parseArrayToMap(arr []Changeset) (map[string]Changeset, error) {
m := make(map[string]Changeset)
func parseArrayToMap(arr []changeset.Info) (map[string]changeset.Info, error) {
m := make(map[string]changeset.Info)

for _, cs := range arr {
id := fmt.Sprintf("%v:%v:%v", cs.author, cs.id, cs.filename)
id := fmt.Sprintf("%v:%v:%v", cs.Author, cs.ID, cs.Filename)
if _, found := m[id]; found {
return nil, errors.New("Duplicate entry found: " + id)
}
Expand All @@ -120,3 +122,26 @@ func parseArrayToMap(arr []Changeset) (map[string]Changeset, error) {

return m, nil
}

// loadChangesets will get the changesets based on the type of migration
// specified during the creation of the Rove object.
func (r *Rove) loadChangesets() (map[string]changeset.Info, error) {
// Use the file to get the changesets first.
if len(r.file) > 0 {
// Get the changesets in a map.
m, err := parseFileToMap(r.file)
if err != nil {
return nil, err
}

return m, nil
}

// Else use the changeset that was passed in.
arr, err := parseReaderToMap(strings.NewReader(r.changeset), elementMemory)
if err != nil {
return nil, err
}

return arr, nil
}
48 changes: 27 additions & 21 deletions changeset.go → pkg/changeset/changeset.go
Original file line number Diff line number Diff line change
@@ -1,69 +1,75 @@
package rove
package changeset

import (
"errors"
"strings"
)

// Changeset is a SQL changeset.
type Changeset struct {
id string
author string
filename string
md5 string
description string
version string
var (
// ErrInvalidHeader is when the changeset header is invalid.
ErrInvalidHeader = errors.New("invalid changeset header")
)

// Info is a database changeset.
type Info struct {
ID string
Author string
Filename string
MD5 string
Description string
Version string

change []string
rollback []string
}

// ParseHeader will parse the header information.
func (cs *Changeset) ParseHeader(line string) error {
func (cs *Info) ParseHeader(line string) error {
arr := strings.Split(line, ":")
if len(arr) != 2 {
return ErrInvalidHeader
}

cs.author = arr[0]
cs.id = arr[1]
cs.Author = arr[0]
cs.ID = arr[1]

return nil
}

// SetFileInfo will set the file information.
func (cs *Changeset) SetFileInfo(filename string, description string, version string) {
cs.filename = filename
cs.description = description
cs.version = version
func (cs *Info) SetFileInfo(filename string, description string, version string) {
cs.Filename = filename
cs.Description = description
cs.Version = version
}

// AddRollback will add a rollback command.
func (cs *Changeset) AddRollback(line string) {
func (cs *Info) AddRollback(line string) {
if len(cs.rollback) == 0 {
cs.rollback = make([]string, 0)
}
cs.rollback = append(cs.rollback, line)
}

// AddChange will add a change command.
func (cs *Changeset) AddChange(line string) {
func (cs *Info) AddChange(line string) {
if len(cs.change) == 0 {
cs.change = make([]string, 0)
}
cs.change = append(cs.change, line)
}

// Changes will return all the changes.
func (cs *Changeset) Changes() string {
func (cs *Info) Changes() string {
return strings.Join(cs.change, "\n")
}

// Rollbacks will return all the rollbacks.
func (cs *Changeset) Rollbacks() string {
func (cs *Info) Rollbacks() string {
return strings.Join(cs.rollback, "\n")
}

// Checksum returns an MD5 checksum for the changeset.
func (cs *Changeset) Checksum() string {
func (cs *Info) Checksum() string {
return md5sum(cs.Changes())
}
2 changes: 1 addition & 1 deletion helper.go → pkg/changeset/helper.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package rove
package changeset

import (
"bytes"
Expand Down
6 changes: 3 additions & 3 deletions pkg/database/mysql.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ func (m *MySQL) Insert(id, author, filename string, count int, checksum, descrip

// Changesets returns a list of the changesets from the database in ascending
// order (false) or descending order (true).
func (m *MySQL) Changesets(reverse bool) ([]rove.DBChangeset, error) {
func (m *MySQL) Changesets(reverse bool) ([]rove.Change, error) {
order := "ASC"
if reverse {
order = "DESC"
Expand All @@ -110,9 +110,9 @@ func (m *MySQL) Changesets(reverse bool) ([]rove.DBChangeset, error) {
ORDER BY orderexecuted `+order)

// Copy from one struct to another.
out := make([]rove.DBChangeset, 0)
out := make([]rove.Change, 0)
for _, i := range results {
out = append(out, rove.DBChangeset(i))
out = append(out, rove.Change(i))
}

return out, err
Expand Down
33 changes: 5 additions & 28 deletions reset.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,6 @@ import (
"strings"
)

// loadChangesets will get the changesets based on the type of migration
// specified during the creation of the Rove object.
func (r *Rove) loadChangesets() (map[string]Changeset, error) {
// Use the file to get the changesets first.
if len(r.file) > 0 {
// Get the changesets in a map.
m, err := parseFileToMap(r.file)
if err != nil {
return nil, err
}

return m, nil
}

// Else use the changeset that was passed in.
arr, err := parseReaderToMap(strings.NewReader(r.changeset), elementMemory)
if err != nil {
return nil, err
}

return arr, nil
}

// Reset will remove all migrations. If max is 0, all rollbacks are run.
func (r *Rove) Reset(max int) error {
// Get the changesets.
Expand Down Expand Up @@ -77,27 +54,27 @@ func (r *Rove) Reset(max int) error {
// Execute the query.
err = tx.Exec(q)
if err != nil {
return fmt.Errorf("sql error on rollback %v:%v - %v", cs.author, cs.id, err.Error())
return fmt.Errorf("sql error on rollback %v:%v - %v", cs.Author, cs.ID, err.Error())
}
}

err = tx.Commit()
if err != nil {
errr := tx.Rollback()
if errr != nil {
return fmt.Errorf("sql error on commit rollback %v:%v - %v", cs.author, cs.id, errr.Error())
return fmt.Errorf("sql error on commit rollback %v:%v - %v", cs.Author, cs.ID, errr.Error())
}
return fmt.Errorf("sql error on commit %v:%v - %v", cs.author, cs.id, err.Error())
return fmt.Errorf("sql error on commit %v:%v - %v", cs.Author, cs.ID, err.Error())
}

// Delete the record.
err = r.db.Delete(cs.id, cs.author, cs.filename)
err = r.db.Delete(cs.ID, cs.Author, cs.Filename)
if err != nil {
return err
}

if r.Verbose {
fmt.Printf("Rollback applied: %v:%v\n", cs.author, cs.id)
fmt.Printf("Rollback applied: %v:%v\n", cs.Author, cs.ID)
}

// Only perform the maxium number of changes based on the max value.
Expand Down
Loading

0 comments on commit 7f88bc5

Please sign in to comment.