migrator.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. package migrator
  2. import (
  3. "time"
  4. _ "github.com/go-sql-driver/mysql"
  5. "github.com/go-xorm/xorm"
  6. "github.com/grafana/grafana/pkg/log"
  7. _ "github.com/lib/pq"
  8. _ "github.com/mattn/go-sqlite3"
  9. )
  10. type Migrator struct {
  11. x *xorm.Engine
  12. dialect Dialect
  13. migrations []Migration
  14. Logger log.Logger
  15. }
  16. type MigrationLog struct {
  17. Id int64
  18. MigrationId string
  19. Sql string
  20. Success bool
  21. Error string
  22. Timestamp time.Time
  23. }
  24. func NewMigrator(engine *xorm.Engine) *Migrator {
  25. mg := &Migrator{}
  26. mg.x = engine
  27. mg.Logger = log.New("migrator")
  28. mg.migrations = make([]Migration, 0)
  29. mg.dialect = NewDialect(mg.x)
  30. return mg
  31. }
  32. func (mg *Migrator) MigrationsCount() int {
  33. return len(mg.migrations)
  34. }
  35. func (mg *Migrator) AddMigration(id string, m Migration) {
  36. m.SetId(id)
  37. mg.migrations = append(mg.migrations, m)
  38. }
  39. func (mg *Migrator) GetMigrationLog() (map[string]MigrationLog, error) {
  40. logMap := make(map[string]MigrationLog)
  41. logItems := make([]MigrationLog, 0)
  42. exists, err := mg.x.IsTableExist(new(MigrationLog))
  43. if err != nil {
  44. return nil, err
  45. }
  46. if !exists {
  47. return logMap, nil
  48. }
  49. if err = mg.x.Find(&logItems); err != nil {
  50. return nil, err
  51. }
  52. for _, logItem := range logItems {
  53. if !logItem.Success {
  54. continue
  55. }
  56. logMap[logItem.MigrationId] = logItem
  57. }
  58. return logMap, nil
  59. }
  60. func (mg *Migrator) Start() error {
  61. mg.Logger.Info("Starting DB migration")
  62. logMap, err := mg.GetMigrationLog()
  63. if err != nil {
  64. return err
  65. }
  66. for _, m := range mg.migrations {
  67. _, exists := logMap[m.Id()]
  68. if exists {
  69. mg.Logger.Debug("Skipping migration: Already executed", "id", m.Id())
  70. continue
  71. }
  72. sql := m.Sql(mg.dialect)
  73. record := MigrationLog{
  74. MigrationId: m.Id(),
  75. Sql: sql,
  76. Timestamp: time.Now(),
  77. }
  78. mg.Logger.Debug("Executing", "sql", sql)
  79. err := mg.inTransaction(func(sess *xorm.Session) error {
  80. err := mg.exec(m, sess)
  81. if err != nil {
  82. mg.Logger.Error("Exec failed", "error", err, "sql", sql)
  83. record.Error = err.Error()
  84. sess.Insert(&record)
  85. return err
  86. }
  87. record.Success = true
  88. sess.Insert(&record)
  89. return nil
  90. })
  91. if err != nil {
  92. return err
  93. }
  94. }
  95. return nil
  96. }
  97. func (mg *Migrator) exec(m Migration, sess *xorm.Session) error {
  98. mg.Logger.Info("Executing migration", "id", m.Id())
  99. condition := m.GetCondition()
  100. if condition != nil {
  101. sql, args := condition.Sql(mg.dialect)
  102. results, err := sess.SQL(sql).Query(args...)
  103. if err != nil || len(results) == 0 {
  104. mg.Logger.Debug("Skipping migration condition not fulfilled", "id", m.Id())
  105. return sess.Rollback()
  106. }
  107. }
  108. _, err := sess.Exec(m.Sql(mg.dialect))
  109. if err != nil {
  110. mg.Logger.Error("Executing migration failed", "id", m.Id(), "error", err)
  111. return err
  112. }
  113. return nil
  114. }
  115. type dbTransactionFunc func(sess *xorm.Session) error
  116. func (mg *Migrator) inTransaction(callback dbTransactionFunc) error {
  117. var err error
  118. sess := mg.x.NewSession()
  119. defer sess.Close()
  120. if err = sess.Begin(); err != nil {
  121. return err
  122. }
  123. err = callback(sess)
  124. if err != nil {
  125. sess.Rollback()
  126. return err
  127. } else if err = sess.Commit(); err != nil {
  128. return err
  129. }
  130. return nil
  131. }