migrate.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. package migrate
  2. import (
  3. "errors"
  4. "fmt"
  5. "github.com/xormplus/xorm"
  6. )
  7. // MigrateFunc is the func signature for migrating.
  8. type MigrateFunc func(*xorm.Engine) error
  9. // RollbackFunc is the func signature for rollbacking.
  10. type RollbackFunc func(*xorm.Engine) error
  11. // InitSchemaFunc is the func signature for initializing the schema.
  12. type InitSchemaFunc func(*xorm.Engine) error
  13. // Options define options for all migrations.
  14. type Options struct {
  15. // TableName is the migration table.
  16. TableName string
  17. // IDColumnName is the name of column where the migration id will be stored.
  18. IDColumnName string
  19. }
  20. // Migration represents a database migration (a modification to be made on the database).
  21. type Migration struct {
  22. // ID is the migration identifier. Usually a timestamp like "201601021504".
  23. ID string
  24. // Migrate is a function that will br executed while running this migration.
  25. Migrate MigrateFunc
  26. // Rollback will be executed on rollback. Can be nil.
  27. Rollback RollbackFunc
  28. }
  29. // Migrate represents a collection of all migrations of a database schema.
  30. type Migrate struct {
  31. db *xorm.Engine
  32. options *Options
  33. migrations []*Migration
  34. initSchema InitSchemaFunc
  35. }
  36. var (
  37. // DefaultOptions can be used if you don't want to think about options.
  38. DefaultOptions = &Options{
  39. TableName: "migrations",
  40. IDColumnName: "id",
  41. }
  42. // ErrRollbackImpossible is returned when trying to rollback a migration
  43. // that has no rollback function.
  44. ErrRollbackImpossible = errors.New("It's impossible to rollback this migration")
  45. // ErrNoMigrationDefined is returned when no migration is defined.
  46. ErrNoMigrationDefined = errors.New("No migration defined")
  47. // ErrMissingID is returned when the ID od migration is equal to ""
  48. ErrMissingID = errors.New("Missing ID in migration")
  49. // ErrNoRunnedMigration is returned when any runned migration was found while
  50. // running RollbackLast
  51. ErrNoRunnedMigration = errors.New("Could not find last runned migration")
  52. )
  53. // New returns a new Gormigrate.
  54. func New(db *xorm.Engine, options *Options, migrations []*Migration) *Migrate {
  55. return &Migrate{
  56. db: db,
  57. options: options,
  58. migrations: migrations,
  59. }
  60. }
  61. // InitSchema sets a function that is run if no migration is found.
  62. // The idea is preventing to run all migrations when a new clean database
  63. // is being migrating. In this function you should create all tables and
  64. // foreign key necessary to your application.
  65. func (m *Migrate) InitSchema(initSchema InitSchemaFunc) {
  66. m.initSchema = initSchema
  67. }
  68. // Migrate executes all migrations that did not run yet.
  69. func (m *Migrate) Migrate() error {
  70. if err := m.createMigrationTableIfNotExists(); err != nil {
  71. return err
  72. }
  73. if m.initSchema != nil && m.isFirstRun() {
  74. if err := m.runInitSchema(); err != nil {
  75. return err
  76. }
  77. return nil
  78. }
  79. for _, migration := range m.migrations {
  80. if err := m.runMigration(migration); err != nil {
  81. return err
  82. }
  83. }
  84. return nil
  85. }
  86. // RollbackLast undo the last migration
  87. func (m *Migrate) RollbackLast() error {
  88. if len(m.migrations) == 0 {
  89. return ErrNoMigrationDefined
  90. }
  91. lastRunnedMigration, err := m.getLastRunnedMigration()
  92. if err != nil {
  93. return err
  94. }
  95. if err := m.RollbackMigration(lastRunnedMigration); err != nil {
  96. return err
  97. }
  98. return nil
  99. }
  100. func (m *Migrate) getLastRunnedMigration() (*Migration, error) {
  101. for i := len(m.migrations) - 1; i >= 0; i-- {
  102. migration := m.migrations[i]
  103. run, err := m.migrationDidRun(migration)
  104. if err != nil {
  105. return nil, err
  106. } else if run {
  107. return migration, nil
  108. }
  109. }
  110. return nil, ErrNoRunnedMigration
  111. }
  112. // RollbackMigration undo a migration.
  113. func (m *Migrate) RollbackMigration(mig *Migration) error {
  114. if mig.Rollback == nil {
  115. return ErrRollbackImpossible
  116. }
  117. if err := mig.Rollback(m.db); err != nil {
  118. return err
  119. }
  120. sql := fmt.Sprintf("DELETE FROM %s WHERE %s = ?", m.options.TableName, m.options.IDColumnName)
  121. if _, err := m.db.Exec(sql, mig.ID); err != nil {
  122. return err
  123. }
  124. return nil
  125. }
  126. func (m *Migrate) runInitSchema() error {
  127. if err := m.initSchema(m.db); err != nil {
  128. return err
  129. }
  130. for _, migration := range m.migrations {
  131. if err := m.insertMigration(migration.ID); err != nil {
  132. return err
  133. }
  134. }
  135. return nil
  136. }
  137. func (m *Migrate) runMigration(migration *Migration) error {
  138. if len(migration.ID) == 0 {
  139. return ErrMissingID
  140. }
  141. run, err :=m.migrationDidRun(migration)
  142. if err != nil {
  143. return err
  144. }
  145. if !run {
  146. if err := migration.Migrate(m.db); err != nil {
  147. return err
  148. }
  149. if err := m.insertMigration(migration.ID); err != nil {
  150. return err
  151. }
  152. }
  153. return nil
  154. }
  155. func (m *Migrate) createMigrationTableIfNotExists() error {
  156. exists, err := m.db.IsTableExist(m.options.TableName)
  157. if err != nil {
  158. return err
  159. }
  160. if exists {
  161. return nil
  162. }
  163. sql := fmt.Sprintf("CREATE TABLE %s (%s VARCHAR(255) PRIMARY KEY)", m.options.TableName, m.options.IDColumnName)
  164. if _, err := m.db.Exec(sql); err != nil {
  165. return err
  166. }
  167. return nil
  168. }
  169. func (m *Migrate) migrationDidRun(mig *Migration) (bool, error) {
  170. count, err := m.db.SQL(fmt.Sprintf("SELECT COUNT(*) FROM %s WHERE %s = ?", m.options.TableName, m.options.IDColumnName), mig.ID).Count()
  171. return count > 0, err
  172. }
  173. func (m *Migrate) isFirstRun() bool {
  174. row := m.db.DB().QueryRow(fmt.Sprintf("SELECT COUNT(*) FROM %s", m.options.TableName))
  175. var count int
  176. row.Scan(&count)
  177. return count == 0
  178. }
  179. func (m *Migrate) insertMigration(id string) error {
  180. sql := fmt.Sprintf("INSERT INTO %s (%s) VALUES (?)", m.options.TableName, m.options.IDColumnName)
  181. _, err := m.db.Exec(sql, id)
  182. return err
  183. }