package database import ( "path" "strconv" "strings" "github.com/pkg/errors" ) func (c connector) Migrate(module string, migrations MigrationStorage) error { m, err := collectMigrations(migrations, "/") if err != nil { return errors.Wrap(err, "collecting migrations") } migrationKey := strings.Join([]string{"migration_state", module}, "-") var lastMigration int if err = c.ReadCoreMeta(migrationKey, &lastMigration); err != nil && !errors.Is(err, ErrCoreMetaNotFound) { return errors.Wrap(err, "getting last migration") } nextMigration := lastMigration for { nextMigration++ filename := m[nextMigration] if filename == "" { break } if err = c.applyMigration(migrations, filename); err != nil { return errors.Wrapf(err, "applying migration %d", nextMigration) } if err = c.StoreCoreMeta(migrationKey, nextMigration); err != nil { return errors.Wrap(err, "updating migration number") } } return nil } func (c connector) applyMigration(migrations MigrationStorage, filename string) error { rawMigration, err := migrations.ReadFile(filename) if err != nil { return errors.Wrap(err, "reading migration file") } _, err = c.db.Exec(string(rawMigration)) return errors.Wrap(err, "executing migration statement(s)") } func collectMigrations(migrations MigrationStorage, dir string) (map[int]string, error) { out := map[int]string{} entries, err := migrations.ReadDir(dir) if err != nil { return nil, errors.Wrapf(err, "reading dir %q", dir) } for _, e := range entries { if e.IsDir() { sout, err := collectMigrations(migrations, path.Join(dir, e.Name())) if err != nil { return nil, errors.Wrapf(err, "scanning subdir %q", e.Name()) } for n, p := range sout { if out[n] != "" { return nil, errors.Errorf("migration %d found more than once", n) } out[n] = p } continue } if !migrationFilename.MatchString(e.Name()) { continue } matches := migrationFilename.FindStringSubmatch(e.Name()) n, err := strconv.Atoi(matches[1]) if err != nil { return nil, errors.Wrap(err, "parsing migration number") } out[n] = path.Join(dir, e.Name()) } return out, nil }