2021-11-21 14:05:07 +01:00
|
|
|
package migrate
|
|
|
|
|
2021-11-24 12:42:18 +01:00
|
|
|
import (
|
2021-11-28 13:52:27 +01:00
|
|
|
"database/sql"
|
2021-11-24 12:42:18 +01:00
|
|
|
"time"
|
|
|
|
|
2022-03-30 20:36:25 +02:00
|
|
|
"github.com/photoprism/photoprism/pkg/list"
|
|
|
|
|
2021-11-28 13:52:27 +01:00
|
|
|
"github.com/dustin/go-humanize/english"
|
2021-11-24 12:42:18 +01:00
|
|
|
"github.com/jinzhu/gorm"
|
|
|
|
)
|
2021-11-21 14:05:07 +01:00
|
|
|
|
|
|
|
// Migrations represents a sorted list of migrations.
|
|
|
|
type Migrations []Migration
|
|
|
|
|
2021-11-28 13:52:27 +01:00
|
|
|
// MigrationMap represents a map of migrations.
|
|
|
|
type MigrationMap map[string]Migration
|
|
|
|
|
|
|
|
// Existing finds and returns previously executed database schema migrations.
|
|
|
|
func Existing(db *gorm.DB) MigrationMap {
|
|
|
|
result := make(MigrationMap)
|
|
|
|
|
2022-01-05 18:15:39 +01:00
|
|
|
stmt := db.Model(Migration{})
|
2021-11-28 13:52:27 +01:00
|
|
|
stmt = stmt.Select("id, dialect, error, source, started_at, finished_at")
|
|
|
|
|
|
|
|
rows, err := stmt.Rows()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
log.Warnf("migrate: %s (find existing)", err)
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
|
|
|
defer func(rows *sql.Rows) {
|
|
|
|
err = rows.Close()
|
|
|
|
}(rows)
|
|
|
|
|
|
|
|
for rows.Next() {
|
|
|
|
m := Migration{}
|
|
|
|
|
|
|
|
if err = rows.Scan(&m.ID, &m.Dialect, &m.Error, &m.Source, &m.StartedAt, &m.FinishedAt); err != nil {
|
|
|
|
log.Warnf("migrate: %s (scan existing)", err)
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
|
|
|
result[m.ID] = m
|
|
|
|
}
|
|
|
|
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2021-11-21 14:05:07 +01:00
|
|
|
// Start runs all migrations that haven't been executed yet.
|
2022-03-30 20:36:25 +02:00
|
|
|
func (m *Migrations) Start(db *gorm.DB, runFailed bool, ids []string) {
|
2021-11-28 13:52:27 +01:00
|
|
|
// Find previously executed migrations.
|
|
|
|
executed := Existing(db)
|
|
|
|
|
2022-01-05 18:15:39 +01:00
|
|
|
if prev := len(executed); prev == 0 {
|
2022-03-30 20:36:25 +02:00
|
|
|
log.Infof("migrate: no previously executed migrations")
|
2022-01-05 18:15:39 +01:00
|
|
|
} else {
|
|
|
|
log.Debugf("migrate: found %s", english.Plural(len(executed), "previous migration", "previous migrations"))
|
|
|
|
}
|
2021-11-28 13:52:27 +01:00
|
|
|
|
2021-11-21 14:05:07 +01:00
|
|
|
for _, migration := range *m {
|
2021-11-24 12:42:18 +01:00
|
|
|
start := time.Now()
|
2022-04-13 22:17:59 +02:00
|
|
|
migration.StartedAt = start.UTC().Truncate(time.Second)
|
2021-11-24 12:42:18 +01:00
|
|
|
|
2022-03-30 20:36:25 +02:00
|
|
|
// Excluded?
|
|
|
|
if list.Excludes(ids, migration.ID) {
|
|
|
|
log.Debugf("migrate: %s skipped", migration.ID)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2021-11-28 13:52:27 +01:00
|
|
|
// Already executed?
|
|
|
|
if done, ok := executed[migration.ID]; ok {
|
2022-04-01 16:02:58 +02:00
|
|
|
// Repeat?
|
|
|
|
if !done.Repeat(runFailed) && !list.Contains(ids, migration.ID) {
|
2021-11-28 13:52:27 +01:00
|
|
|
log.Debugf("migrate: %s skipped", migration.ID)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
} else if err := db.Create(migration).Error; err != nil {
|
|
|
|
// Should not happen.
|
|
|
|
log.Warnf("migrate: creating %s failed with %s [%s]", migration.ID, err, time.Since(start))
|
2021-11-24 12:42:18 +01:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2021-11-28 13:52:27 +01:00
|
|
|
// Run migration.
|
2021-11-24 12:42:18 +01:00
|
|
|
if err := migration.Execute(db); err != nil {
|
|
|
|
migration.Fail(err, db)
|
2021-11-28 13:52:27 +01:00
|
|
|
log.Errorf("migrate: executing %s failed with %s [%s]", migration.ID, err, time.Since(start))
|
2021-11-24 12:42:18 +01:00
|
|
|
} else if err = migration.Finish(db); err != nil {
|
2021-11-28 13:52:27 +01:00
|
|
|
log.Warnf("migrate: updating %s failed with %s [%s]", migration.ID, err, time.Since(start))
|
2021-11-24 12:42:18 +01:00
|
|
|
} else {
|
2021-11-28 13:52:27 +01:00
|
|
|
log.Infof("migrate: %s successful [%s]", migration.ID, time.Since(start))
|
2021-11-24 12:42:18 +01:00
|
|
|
}
|
2021-11-21 14:05:07 +01:00
|
|
|
}
|
|
|
|
}
|