Skip to content

Commit a8bcd23

Browse files
committed
Account for "holes" in the existing migrations in PlanMigration
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.
1 parent 12b7097 commit a8bcd23

File tree

2 files changed

+108
-9
lines changed

2 files changed

+108
-9
lines changed

migrate.go

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -323,23 +323,32 @@ func PlanMigration(db *sql.DB, dialect string, m MigrationSource, dir MigrationD
323323
record = existingMigrations[len(existingMigrations)-1]
324324
}
325325

326-
// Figure out which of the supplied migrations has been applied.
326+
result := make([]*PlannedMigration, 0)
327+
328+
// Add missing migrations up to the last run migration.
329+
// This can happen for example when merges happened.
330+
if len(existingMigrations) > 0 {
331+
result = append(result, ToCatchup(migrations, existingMigrations, record)...)
332+
}
333+
334+
// Figure out which migrations to apply
327335
toApply := ToApply(migrations, record.Id, dir)
328336
toApplyCount := len(toApply)
329337
if max > 0 && max < toApplyCount {
330338
toApplyCount = max
331339
}
332-
333-
result := make([]*PlannedMigration, toApplyCount)
334-
for k, v := range toApply[0:toApplyCount] {
335-
result[k] = &PlannedMigration{
336-
Migration: v,
337-
}
340+
for _, v := range toApply[0:toApplyCount] {
338341

339342
if dir == Up {
340-
result[k].Queries = v.Up
343+
result = append(result, &PlannedMigration{
344+
Migration: v,
345+
Queries: v.Up,
346+
})
341347
} else if dir == Down {
342-
result[k].Queries = v.Down
348+
result = append(result, &PlannedMigration{
349+
Migration: v,
350+
Queries: v.Down,
351+
})
343352
}
344353
}
345354

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

388+
func ToCatchup(migrations, existingMigrations []*Migration, lastRun *Migration) []*PlannedMigration {
389+
missing := make([]*PlannedMigration, 0)
390+
for _, migration := range migrations {
391+
found := false
392+
for _, existing := range existingMigrations {
393+
if existing.Id == migration.Id {
394+
found = true
395+
break
396+
}
397+
}
398+
if !found {
399+
if migration.Less(lastRun) {
400+
missing = append(missing, &PlannedMigration{Migration: migration, Queries: migration.Up})
401+
}
402+
}
403+
}
404+
return missing
405+
}
406+
379407
func GetMigrationRecords(db *sql.DB, dialect string) ([]*MigrationRecord, error) {
380408
dbMap, err := getMigrationDbMap(db, dialect)
381409
if err != nil {

migrate_test.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,3 +284,74 @@ func (s *SqliteMigrateSuite) TestPlanMigration(c *C) {
284284
c.Assert(plannedMigrations[1].Migration, Equals, migrations.Migrations[1])
285285
c.Assert(plannedMigrations[2].Migration, Equals, migrations.Migrations[0])
286286
}
287+
288+
func (s *SqliteMigrateSuite) TestPlanMigrationWithHoles(c *C) {
289+
up := "SELECT 0"
290+
down := "SELECT 1"
291+
migrations := &MemoryMigrationSource{
292+
Migrations: []*Migration{
293+
&Migration{
294+
Id: "1",
295+
Up: []string{up},
296+
Down: []string{down},
297+
},
298+
&Migration{
299+
Id: "3",
300+
Up: []string{up},
301+
Down: []string{down},
302+
},
303+
},
304+
}
305+
n, err := Exec(s.Db, "sqlite3", migrations, Up)
306+
c.Assert(err, IsNil)
307+
c.Assert(n, Equals, 2)
308+
309+
migrations.Migrations = append(migrations.Migrations, &Migration{
310+
Id: "2",
311+
Up: []string{up},
312+
Down: []string{down},
313+
})
314+
315+
migrations.Migrations = append(migrations.Migrations, &Migration{
316+
Id: "4",
317+
Up: []string{up},
318+
Down: []string{down},
319+
})
320+
321+
migrations.Migrations = append(migrations.Migrations, &Migration{
322+
Id: "5",
323+
Up: []string{up},
324+
Down: []string{down},
325+
})
326+
327+
// apply all the missing migrations
328+
plannedMigrations, _, err := PlanMigration(s.Db, "sqlite3", migrations, Up, 0)
329+
c.Assert(err, IsNil)
330+
c.Assert(plannedMigrations, HasLen, 3)
331+
c.Assert(plannedMigrations[0].Migration.Id, Equals, "2")
332+
c.Assert(plannedMigrations[0].Queries[0], Equals, up)
333+
c.Assert(plannedMigrations[1].Migration.Id, Equals, "4")
334+
c.Assert(plannedMigrations[1].Queries[0], Equals, up)
335+
c.Assert(plannedMigrations[2].Migration.Id, Equals, "5")
336+
c.Assert(plannedMigrations[2].Queries[0], Equals, up)
337+
338+
// first catch up to current target state 123, then migrate down 1 step to 12
339+
plannedMigrations, _, err = PlanMigration(s.Db, "sqlite3", migrations, Down, 1)
340+
c.Assert(err, IsNil)
341+
c.Assert(plannedMigrations, HasLen, 2)
342+
c.Assert(plannedMigrations[0].Migration.Id, Equals, "2")
343+
c.Assert(plannedMigrations[0].Queries[0], Equals, up)
344+
c.Assert(plannedMigrations[1].Migration.Id, Equals, "3")
345+
c.Assert(plannedMigrations[1].Queries[0], Equals, down)
346+
347+
// first catch up to current target state 123, then migrate down 2 steps to 1
348+
plannedMigrations, _, err = PlanMigration(s.Db, "sqlite3", migrations, Down, 2)
349+
c.Assert(err, IsNil)
350+
c.Assert(plannedMigrations, HasLen, 3)
351+
c.Assert(plannedMigrations[0].Migration.Id, Equals, "2")
352+
c.Assert(plannedMigrations[0].Queries[0], Equals, up)
353+
c.Assert(plannedMigrations[1].Migration.Id, Equals, "3")
354+
c.Assert(plannedMigrations[1].Queries[0], Equals, down)
355+
c.Assert(plannedMigrations[2].Migration.Id, Equals, "2")
356+
c.Assert(plannedMigrations[2].Queries[0], Equals, down)
357+
}

0 commit comments

Comments
 (0)