migrator.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  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.DriverName())
  30. return mg
  31. }
  32. func (mg *Migrator) AddMigration(id string, m Migration) {
  33. m.SetId(id)
  34. mg.migrations = append(mg.migrations, m)
  35. }
  36. func (mg *Migrator) GetMigrationLog() (map[string]MigrationLog, error) {
  37. logMap := make(map[string]MigrationLog)
  38. logItems := make([]MigrationLog, 0)
  39. exists, err := mg.x.IsTableExist(new(MigrationLog))
  40. if err != nil {
  41. return nil, err
  42. }
  43. if !exists {
  44. return logMap, nil
  45. }
  46. if err = mg.x.Find(&logItems); err != nil {
  47. return nil, err
  48. }
  49. for _, logItem := range logItems {
  50. if !logItem.Success {
  51. continue
  52. }
  53. logMap[logItem.MigrationId] = logItem
  54. }
  55. return logMap, nil
  56. }
  57. func (mg *Migrator) Start() error {
  58. mg.Logger.Info("Starting DB migration")
  59. logMap, err := mg.GetMigrationLog()
  60. if err != nil {
  61. return err
  62. }
  63. for _, m := range mg.migrations {
  64. _, exists := logMap[m.Id()]
  65. if exists {
  66. mg.Logger.Debug("Skipping migration: Already executed", "id", m.Id())
  67. continue
  68. }
  69. sql := m.Sql(mg.dialect)
  70. record := MigrationLog{
  71. MigrationId: m.Id(),
  72. Sql: sql,
  73. Timestamp: time.Now(),
  74. }
  75. mg.Logger.Debug("Executing", "sql", sql)
  76. if err := mg.exec(m); err != nil {
  77. mg.Logger.Error("Exec failed", "error", err, "sql", sql)
  78. record.Error = err.Error()
  79. mg.x.Insert(&record)
  80. return err
  81. } else {
  82. record.Success = true
  83. mg.x.Insert(&record)
  84. }
  85. }
  86. return nil
  87. }
  88. func (mg *Migrator) exec(m Migration) error {
  89. mg.Logger.Info("Executing migration", "id", m.Id())
  90. err := mg.inTransaction(func(sess *xorm.Session) error {
  91. condition := m.GetCondition()
  92. if condition != nil {
  93. sql, args := condition.Sql(mg.dialect)
  94. results, err := sess.Query(sql, args...)
  95. if err != nil || len(results) == 0 {
  96. mg.Logger.Info("Skipping migration condition not fulfilled", "id", m.Id())
  97. return sess.Rollback()
  98. }
  99. }
  100. _, err := sess.Exec(m.Sql(mg.dialect))
  101. if err != nil {
  102. mg.Logger.Error("Executing migration failed", "id", m.Id(), "error", err)
  103. return err
  104. }
  105. return nil
  106. })
  107. if err != nil {
  108. return err
  109. }
  110. return nil
  111. }
  112. type dbTransactionFunc func(sess *xorm.Session) error
  113. func (mg *Migrator) inTransaction(callback dbTransactionFunc) error {
  114. var err error
  115. sess := mg.x.NewSession()
  116. defer sess.Close()
  117. if err = sess.Begin(); err != nil {
  118. return err
  119. }
  120. err = callback(sess)
  121. if err != nil {
  122. sess.Rollback()
  123. return err
  124. } else if err = sess.Commit(); err != nil {
  125. return err
  126. }
  127. return nil
  128. }