Skip to content

Commit

Permalink
Account for "holes" in the existing migrations in PlanMigration
Browse files Browse the repository at this point in the history
If there are migrations missing before the last run migration, for
example because of a merge, migrate up to the latest run migration
first before applying the user-requested migrations.

Example: if the existing migrations in the database are:

- 1
- 3

and the existing migrations on disk are:

- 1
- 2
- 3
- 4
- 5

For "up, n=0" (3=>5) one would expect these migrations to run:
- up 2
- up 4
- up 5

For "up, n=1" (3=>4) one would expect:
- up 2
- up 4

For "down, n=1" (3=>2) one would expect:
- up 2
- down 3
- down 2

etc.
  • Loading branch information
pschyska committed Mar 26, 2015
1 parent 12b7097 commit a8bcd23
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 9 deletions.
46 changes: 37 additions & 9 deletions migrate.go
Original file line number Diff line number Diff line change
Expand Up @@ -323,23 +323,32 @@ func PlanMigration(db *sql.DB, dialect string, m MigrationSource, dir MigrationD
record = existingMigrations[len(existingMigrations)-1]
}

// Figure out which of the supplied migrations has been applied.
result := make([]*PlannedMigration, 0)

// Add missing migrations up to the last run migration.
// This can happen for example when merges happened.
if len(existingMigrations) > 0 {
result = append(result, ToCatchup(migrations, existingMigrations, record)...)
}

// Figure out which migrations to apply
toApply := ToApply(migrations, record.Id, dir)
toApplyCount := len(toApply)
if max > 0 && max < toApplyCount {
toApplyCount = max
}

result := make([]*PlannedMigration, toApplyCount)
for k, v := range toApply[0:toApplyCount] {
result[k] = &PlannedMigration{
Migration: v,
}
for _, v := range toApply[0:toApplyCount] {

if dir == Up {
result[k].Queries = v.Up
result = append(result, &PlannedMigration{
Migration: v,
Queries: v.Up,
})
} else if dir == Down {
result[k].Queries = v.Down
result = append(result, &PlannedMigration{
Migration: v,
Queries: v.Down,
})
}
}

Expand Down Expand Up @@ -376,6 +385,25 @@ func ToApply(migrations []*Migration, current string, direction MigrationDirecti
panic("Not possible")
}

func ToCatchup(migrations, existingMigrations []*Migration, lastRun *Migration) []*PlannedMigration {
missing := make([]*PlannedMigration, 0)
for _, migration := range migrations {
found := false
for _, existing := range existingMigrations {
if existing.Id == migration.Id {
found = true
break
}
}
if !found {
if migration.Less(lastRun) {
missing = append(missing, &PlannedMigration{Migration: migration, Queries: migration.Up})
}
}
}
return missing
}

func GetMigrationRecords(db *sql.DB, dialect string) ([]*MigrationRecord, error) {
dbMap, err := getMigrationDbMap(db, dialect)
if err != nil {
Expand Down
71 changes: 71 additions & 0 deletions migrate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -284,3 +284,74 @@ func (s *SqliteMigrateSuite) TestPlanMigration(c *C) {
c.Assert(plannedMigrations[1].Migration, Equals, migrations.Migrations[1])
c.Assert(plannedMigrations[2].Migration, Equals, migrations.Migrations[0])
}

func (s *SqliteMigrateSuite) TestPlanMigrationWithHoles(c *C) {
up := "SELECT 0"
down := "SELECT 1"
migrations := &MemoryMigrationSource{
Migrations: []*Migration{
&Migration{
Id: "1",
Up: []string{up},
Down: []string{down},
},
&Migration{
Id: "3",
Up: []string{up},
Down: []string{down},
},
},
}
n, err := Exec(s.Db, "sqlite3", migrations, Up)
c.Assert(err, IsNil)
c.Assert(n, Equals, 2)

migrations.Migrations = append(migrations.Migrations, &Migration{
Id: "2",
Up: []string{up},
Down: []string{down},
})

migrations.Migrations = append(migrations.Migrations, &Migration{
Id: "4",
Up: []string{up},
Down: []string{down},
})

migrations.Migrations = append(migrations.Migrations, &Migration{
Id: "5",
Up: []string{up},
Down: []string{down},
})

// apply all the missing migrations
plannedMigrations, _, err := PlanMigration(s.Db, "sqlite3", migrations, Up, 0)
c.Assert(err, IsNil)
c.Assert(plannedMigrations, HasLen, 3)
c.Assert(plannedMigrations[0].Migration.Id, Equals, "2")
c.Assert(plannedMigrations[0].Queries[0], Equals, up)
c.Assert(plannedMigrations[1].Migration.Id, Equals, "4")
c.Assert(plannedMigrations[1].Queries[0], Equals, up)
c.Assert(plannedMigrations[2].Migration.Id, Equals, "5")
c.Assert(plannedMigrations[2].Queries[0], Equals, up)

// first catch up to current target state 123, then migrate down 1 step to 12
plannedMigrations, _, err = PlanMigration(s.Db, "sqlite3", migrations, Down, 1)
c.Assert(err, IsNil)
c.Assert(plannedMigrations, HasLen, 2)
c.Assert(plannedMigrations[0].Migration.Id, Equals, "2")
c.Assert(plannedMigrations[0].Queries[0], Equals, up)
c.Assert(plannedMigrations[1].Migration.Id, Equals, "3")
c.Assert(plannedMigrations[1].Queries[0], Equals, down)

// first catch up to current target state 123, then migrate down 2 steps to 1
plannedMigrations, _, err = PlanMigration(s.Db, "sqlite3", migrations, Down, 2)
c.Assert(err, IsNil)
c.Assert(plannedMigrations, HasLen, 3)
c.Assert(plannedMigrations[0].Migration.Id, Equals, "2")
c.Assert(plannedMigrations[0].Queries[0], Equals, up)
c.Assert(plannedMigrations[1].Migration.Id, Equals, "3")
c.Assert(plannedMigrations[1].Queries[0], Equals, down)
c.Assert(plannedMigrations[2].Migration.Id, Equals, "2")
c.Assert(plannedMigrations[2].Queries[0], Equals, down)
}

0 comments on commit a8bcd23

Please sign in to comment.