Skip to content

Commit 5136bb3

Browse files
committed
Handle unknown migrations in ToApply()
1 parent 6f47575 commit 5136bb3

3 files changed

Lines changed: 168 additions & 40 deletions

File tree

migrate.go

Lines changed: 67 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -364,9 +364,11 @@ func PlanMigration(db *sql.DB, dialect string, m MigrationSource, dir MigrationD
364364
if len(existingMigrations) > 0 {
365365
result = append(result, ToCatchup(migrations, existingMigrations, record)...)
366366
}
367-
368367
// Figure out which migrations to apply
369-
toApply := ToApply(migrations, record.Id, dir)
368+
toApply := ToApply(migrations, existingMigrations, dir)
369+
if err != nil {
370+
return nil, nil, err
371+
}
370372
toApplyCount := len(toApply)
371373
if max > 0 && max < toApplyCount {
372374
toApplyCount = max
@@ -379,6 +381,10 @@ func PlanMigration(db *sql.DB, dialect string, m MigrationSource, dir MigrationD
379381
Queries: v.Up,
380382
})
381383
} else if dir == Down {
384+
if v.Down == nil {
385+
// This happens if we are trying to migrate down a migration unknown to us
386+
return nil, nil, fmt.Errorf("Unable to undo unknown migration %q", v.Id)
387+
}
382388
result = append(result, &PlannedMigration{
383389
Migration: v,
384390
Queries: v.Down,
@@ -389,34 +395,74 @@ func PlanMigration(db *sql.DB, dialect string, m MigrationSource, dir MigrationD
389395
return result, dbMap, nil
390396
}
391397

392-
// Filter a slice of migrations into ones that should be applied.
393-
func ToApply(migrations []*Migration, current string, direction MigrationDirection) []*Migration {
394-
var index = -1
395-
if current != "" {
396-
for index < len(migrations)-1 {
397-
index++
398-
if migrations[index].Id == current {
398+
// Searches the arrays for the latest elements with common ids.
399+
// Returns the indexes of the common id.
400+
func lastCommon(left []*Migration, right []*Migration) (int, int) {
401+
xIndexMatch := -1
402+
yIndexMatch := 0
403+
for i := 0; i < len(left); i++ {
404+
existingId := left[i].Id
405+
for j := yIndexMatch; j < len(right); j++ {
406+
if right[j].Id == existingId {
407+
xIndexMatch = i
408+
yIndexMatch = j
399409
break
400410
}
401411
}
402412
}
413+
if xIndexMatch == -1 {
414+
return -1, -1 // We never found a match; the arrays have nothing in common
415+
}
416+
return xIndexMatch, yIndexMatch
417+
}
403418

419+
// Filter a slice of migrations into ones that should be applied.
420+
func ToApply(migrations, existingMigrations []*Migration, direction MigrationDirection) []*Migration {
404421
if direction == Up {
405-
return migrations[index+1:]
406-
} else if direction == Down {
407-
if index == -1 {
408-
return []*Migration{}
409-
}
422+
return toApplyUp(migrations, existingMigrations)
423+
}
424+
if direction == Down {
425+
return toApplyDown(migrations, existingMigrations)
426+
}
427+
panic("Unknown direction")
428+
}
410429

411-
// Add in reverse order
412-
toApply := make([]*Migration, index+1)
413-
for i := 0; i < index+1; i++ {
414-
toApply[index-i] = migrations[i]
415-
}
416-
return toApply
430+
func toApplyUp(migrations, existingMigrations []*Migration) []*Migration {
431+
if len(existingMigrations) == 0 {
432+
return migrations
433+
}
434+
_, index := lastCommon(existingMigrations, migrations)
435+
return migrations[index+1:]
436+
}
437+
438+
func toApplyDown(migrations, existingMigrations []*Migration) []*Migration {
439+
if len(existingMigrations) == -1 {
440+
return []*Migration{}
441+
}
442+
// When a Migration appears in both existingMigrations and migrations,
443+
// we want the Migration from migrations, since only that list has
444+
// the Up and Down fields set.
445+
migrationsMap := map[string]*Migration{}
446+
for _, m := range existingMigrations {
447+
// existingMigrations will not have Up or Down set
448+
migrationsMap[m.Id] = m
449+
}
450+
for _, m := range migrations {
451+
migrationsMap[m.Id] = m
417452
}
453+
allMigrations := make([]*Migration, 0, len(migrationsMap))
454+
for _, m := range migrationsMap {
455+
allMigrations = append(allMigrations, m)
456+
}
457+
sort.Sort(byId(allMigrations))
418458

419-
panic("Not possible")
459+
_, index := lastCommon(existingMigrations, allMigrations)
460+
// Add in reverse order
461+
toApply := make([]*Migration, index+1)
462+
for i := 0; i < index+1; i++ {
463+
toApply[index-i] = allMigrations[i]
464+
}
465+
return toApply
420466
}
421467

422468
func ToCatchup(migrations, existingMigrations []*Migration, lastRun *Migration) []*PlannedMigration {

migrate_test.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,97 @@ func (s *SqliteMigrateSuite) TestPlanMigrationWithHoles(c *C) {
360360
c.Assert(plannedMigrations[2].Queries[0], Equals, down)
361361
}
362362

363+
func (s *SqliteMigrateSuite) TestMigrationWithMostRecentUnknown(c *C) {
364+
up := "SELECT 0"
365+
down := "SELECT 1"
366+
migrations := &MemoryMigrationSource{
367+
Migrations: []*Migration{
368+
&Migration{
369+
Id: "1",
370+
Up: []string{up},
371+
Down: []string{down},
372+
},
373+
&Migration{
374+
Id: "3",
375+
Up: []string{up},
376+
Down: []string{down},
377+
},
378+
&Migration{
379+
Id: "4",
380+
Up: []string{up},
381+
Down: []string{down},
382+
},
383+
},
384+
}
385+
n, err := Exec(s.Db, "sqlite3", migrations, Up)
386+
c.Assert(err, IsNil)
387+
c.Assert(n, Equals, 3)
388+
389+
// remove "4" from our list of known migrations
390+
migrations.Migrations = migrations.Migrations[0:2]
391+
392+
migrations.Migrations = append(migrations.Migrations, &Migration{
393+
Id: "2",
394+
Up: []string{up},
395+
Down: []string{down},
396+
}, &Migration{
397+
Id: "5",
398+
Up: []string{up},
399+
Down: []string{down},
400+
})
401+
402+
// apply the latest migration
403+
plannedMigrations, _, err := PlanMigration(s.Db, "sqlite3", migrations, Up, 0)
404+
c.Assert(err, IsNil)
405+
c.Assert(plannedMigrations, HasLen, 2)
406+
c.Assert(plannedMigrations[0].Migration.Id, Equals, "2")
407+
c.Assert(plannedMigrations[1].Migration.Id, Equals, "5")
408+
409+
// The most recent migration is unknown; we can't undo it
410+
plannedMigrations, _, err = PlanMigration(s.Db, "sqlite3", migrations, Down, 1)
411+
c.Assert(err, Not(IsNil))
412+
}
413+
414+
func (s *SqliteMigrateSuite) TestMigrationDownWithMiddleUnknown(c *C) {
415+
up := "SELECT 0"
416+
down := "SELECT 1"
417+
migrations := &MemoryMigrationSource{
418+
Migrations: []*Migration{
419+
&Migration{
420+
Id: "1",
421+
Up: []string{up},
422+
Down: []string{down},
423+
},
424+
&Migration{
425+
Id: "2",
426+
Up: []string{up},
427+
Down: []string{down},
428+
},
429+
&Migration{
430+
Id: "3",
431+
Up: []string{up},
432+
Down: []string{down},
433+
},
434+
},
435+
}
436+
n, err := Exec(s.Db, "sqlite3", migrations, Up)
437+
c.Assert(err, IsNil)
438+
c.Assert(n, Equals, 3)
439+
440+
// remove "2" from our list of known migrations
441+
migrations.Migrations = []*Migration{migrations.Migrations[0], migrations.Migrations[2]}
442+
443+
// undo "3"
444+
plannedMigrations, _, err := PlanMigration(s.Db, "sqlite3", migrations, Down, 1)
445+
c.Assert(err, IsNil)
446+
c.Assert(plannedMigrations, HasLen, 1)
447+
c.Assert(plannedMigrations[0].Id, Equals, "3")
448+
449+
// will undo "3", then try "2", but "2" is unknown
450+
plannedMigrations, _, err = PlanMigration(s.Db, "sqlite3", migrations, Down, 2)
451+
c.Assert(err, Not(IsNil))
452+
}
453+
363454
func (s *SqliteMigrateSuite) TestLess(c *C) {
364455
c.Assert((Migration{Id: "1"}).Less(&Migration{Id: "2"}), Equals, true) // 1 less than 2
365456
c.Assert((Migration{Id: "2"}).Less(&Migration{Id: "1"}), Equals, false) // 2 not less than 1

toapply_test.go

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,60 +17,51 @@ type ToApplyMigrateSuite struct {
1717
var _ = Suite(&ToApplyMigrateSuite{})
1818

1919
func (s *ToApplyMigrateSuite) TestGetAll(c *C) {
20-
toApply := ToApply(toapplyMigrations, "", Up)
20+
toApply := ToApply(toapplyMigrations, toapplyMigrations[0:0], Up)
2121
c.Assert(toApply, HasLen, 3)
2222
c.Assert(toApply[0], Equals, toapplyMigrations[0])
2323
c.Assert(toApply[1], Equals, toapplyMigrations[1])
2424
c.Assert(toApply[2], Equals, toapplyMigrations[2])
2525
}
2626

2727
func (s *ToApplyMigrateSuite) TestGetAbc(c *C) {
28-
toApply := ToApply(toapplyMigrations, "abc", Up)
28+
toApply := ToApply(toapplyMigrations, toapplyMigrations[0:1], Up)
2929
c.Assert(toApply, HasLen, 2)
3030
c.Assert(toApply[0], Equals, toapplyMigrations[1])
3131
c.Assert(toApply[1], Equals, toapplyMigrations[2])
3232
}
3333

3434
func (s *ToApplyMigrateSuite) TestGetCde(c *C) {
35-
toApply := ToApply(toapplyMigrations, "cde", Up)
35+
toApply := ToApply(toapplyMigrations, toapplyMigrations[0:2], Up)
3636
c.Assert(toApply, HasLen, 1)
3737
c.Assert(toApply[0], Equals, toapplyMigrations[2])
3838
}
3939

4040
func (s *ToApplyMigrateSuite) TestGetDone(c *C) {
41-
toApply := ToApply(toapplyMigrations, "efg", Up)
42-
c.Assert(toApply, HasLen, 0)
43-
44-
toApply = ToApply(toapplyMigrations, "zzz", Up)
41+
toApply := ToApply(toapplyMigrations, toapplyMigrations[0:3], Up)
4542
c.Assert(toApply, HasLen, 0)
4643
}
4744

4845
func (s *ToApplyMigrateSuite) TestDownDone(c *C) {
49-
toApply := ToApply(toapplyMigrations, "", Down)
46+
toApply := ToApply(toapplyMigrations, toapplyMigrations[0:0], Down)
5047
c.Assert(toApply, HasLen, 0)
5148
}
5249

5350
func (s *ToApplyMigrateSuite) TestDownCde(c *C) {
54-
toApply := ToApply(toapplyMigrations, "cde", Down)
51+
toApply := ToApply(toapplyMigrations, toapplyMigrations[0:2], Down)
5552
c.Assert(toApply, HasLen, 2)
5653
c.Assert(toApply[0], Equals, toapplyMigrations[1])
5754
c.Assert(toApply[1], Equals, toapplyMigrations[0])
5855
}
5956

6057
func (s *ToApplyMigrateSuite) TestDownAbc(c *C) {
61-
toApply := ToApply(toapplyMigrations, "abc", Down)
58+
toApply := ToApply(toapplyMigrations, toapplyMigrations[0:1], Down)
6259
c.Assert(toApply, HasLen, 1)
6360
c.Assert(toApply[0], Equals, toapplyMigrations[0])
6461
}
6562

6663
func (s *ToApplyMigrateSuite) TestDownAll(c *C) {
67-
toApply := ToApply(toapplyMigrations, "efg", Down)
68-
c.Assert(toApply, HasLen, 3)
69-
c.Assert(toApply[0], Equals, toapplyMigrations[2])
70-
c.Assert(toApply[1], Equals, toapplyMigrations[1])
71-
c.Assert(toApply[2], Equals, toapplyMigrations[0])
72-
73-
toApply = ToApply(toapplyMigrations, "zzz", Down)
64+
toApply := ToApply(toapplyMigrations, toapplyMigrations, Down)
7465
c.Assert(toApply, HasLen, 3)
7566
c.Assert(toApply[0], Equals, toapplyMigrations[2])
7667
c.Assert(toApply[1], Equals, toapplyMigrations[1])
@@ -88,13 +79,13 @@ func (s *ToApplyMigrateSuite) TestAlphaNumericMigrations(c *C) {
8879

8980
sort.Sort(migrations)
9081

91-
toApplyUp := ToApply(migrations, "2_cde", Up)
82+
toApplyUp := ToApply(migrations, migrations[0:2], Up)
9283
c.Assert(toApplyUp, HasLen, 3)
9384
c.Assert(toApplyUp[0].Id, Equals, "10_abc")
9485
c.Assert(toApplyUp[1].Id, Equals, "35_cde")
9586
c.Assert(toApplyUp[2].Id, Equals, "efg")
9687

97-
toApplyDown := ToApply(migrations, "2_cde", Down)
88+
toApplyDown := ToApply(migrations, migrations[0:2], Down)
9889
c.Assert(toApplyDown, HasLen, 2)
9990
c.Assert(toApplyDown[0].Id, Equals, "2_cde")
10091
c.Assert(toApplyDown[1].Id, Equals, "1_abc")

0 commit comments

Comments
 (0)