1
0

migration.go 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. // Copyright 2014 beego Author. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. // Package migration is used for migration
  15. //
  16. // The table structure is as follow:
  17. //
  18. // CREATE TABLE `migrations` (
  19. // `id_migration` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'surrogate key',
  20. // `name` varchar(255) DEFAULT NULL COMMENT 'migration name, unique',
  21. // `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'date migrated or rolled back',
  22. // `statements` longtext COMMENT 'SQL statements for this migration',
  23. // `rollback_statements` longtext,
  24. // `status` enum('update','rollback') DEFAULT NULL COMMENT 'update indicates it is a normal migration while rollback means this migration is rolled back',
  25. // PRIMARY KEY (`id_migration`)
  26. // ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  27. package migration
  28. import (
  29. "errors"
  30. "sort"
  31. "strings"
  32. "time"
  33. "github.com/astaxie/beego/logs"
  34. "github.com/astaxie/beego/orm"
  35. )
  36. // const the data format for the bee generate migration datatype
  37. const (
  38. DateFormat = "20060102_150405"
  39. DBDateFormat = "2006-01-02 15:04:05"
  40. )
  41. // Migrationer is an interface for all Migration struct
  42. type Migrationer interface {
  43. Up()
  44. Down()
  45. Reset()
  46. Exec(name, status string) error
  47. GetCreated() int64
  48. }
  49. var (
  50. migrationMap map[string]Migrationer
  51. )
  52. func init() {
  53. migrationMap = make(map[string]Migrationer)
  54. }
  55. // Migration the basic type which will implement the basic type
  56. type Migration struct {
  57. sqls []string
  58. Created string
  59. }
  60. // Up implement in the Inheritance struct for upgrade
  61. func (m *Migration) Up() {
  62. }
  63. // Down implement in the Inheritance struct for down
  64. func (m *Migration) Down() {
  65. }
  66. // SQL add sql want to execute
  67. func (m *Migration) SQL(sql string) {
  68. m.sqls = append(m.sqls, sql)
  69. }
  70. // Reset the sqls
  71. func (m *Migration) Reset() {
  72. m.sqls = make([]string, 0)
  73. }
  74. // Exec execute the sql already add in the sql
  75. func (m *Migration) Exec(name, status string) error {
  76. o := orm.NewOrm()
  77. for _, s := range m.sqls {
  78. logs.Info("exec sql:", s)
  79. r := o.Raw(s)
  80. _, err := r.Exec()
  81. if err != nil {
  82. return err
  83. }
  84. }
  85. return m.addOrUpdateRecord(name, status)
  86. }
  87. func (m *Migration) addOrUpdateRecord(name, status string) error {
  88. o := orm.NewOrm()
  89. if status == "down" {
  90. status = "rollback"
  91. p, err := o.Raw("update migrations set status = ?, rollback_statements = ?, created_at = ? where name = ?").Prepare()
  92. if err != nil {
  93. return nil
  94. }
  95. _, err = p.Exec(status, strings.Join(m.sqls, "; "), time.Now().Format(DBDateFormat), name)
  96. return err
  97. }
  98. status = "update"
  99. p, err := o.Raw("insert into migrations(name, created_at, statements, status) values(?,?,?,?)").Prepare()
  100. if err != nil {
  101. return err
  102. }
  103. _, err = p.Exec(name, time.Now().Format(DBDateFormat), strings.Join(m.sqls, "; "), status)
  104. return err
  105. }
  106. // GetCreated get the unixtime from the Created
  107. func (m *Migration) GetCreated() int64 {
  108. t, err := time.Parse(DateFormat, m.Created)
  109. if err != nil {
  110. return 0
  111. }
  112. return t.Unix()
  113. }
  114. // Register register the Migration in the map
  115. func Register(name string, m Migrationer) error {
  116. if _, ok := migrationMap[name]; ok {
  117. return errors.New("already exist name:" + name)
  118. }
  119. migrationMap[name] = m
  120. return nil
  121. }
  122. // Upgrade upgrate the migration from lasttime
  123. func Upgrade(lasttime int64) error {
  124. sm := sortMap(migrationMap)
  125. i := 0
  126. for _, v := range sm {
  127. if v.created > lasttime {
  128. logs.Info("start upgrade", v.name)
  129. v.m.Reset()
  130. v.m.Up()
  131. err := v.m.Exec(v.name, "up")
  132. if err != nil {
  133. logs.Error("execute error:", err)
  134. time.Sleep(2 * time.Second)
  135. return err
  136. }
  137. logs.Info("end upgrade:", v.name)
  138. i++
  139. }
  140. }
  141. logs.Info("total success upgrade:", i, " migration")
  142. time.Sleep(2 * time.Second)
  143. return nil
  144. }
  145. // Rollback rollback the migration by the name
  146. func Rollback(name string) error {
  147. if v, ok := migrationMap[name]; ok {
  148. logs.Info("start rollback")
  149. v.Reset()
  150. v.Down()
  151. err := v.Exec(name, "down")
  152. if err != nil {
  153. logs.Error("execute error:", err)
  154. time.Sleep(2 * time.Second)
  155. return err
  156. }
  157. logs.Info("end rollback")
  158. time.Sleep(2 * time.Second)
  159. return nil
  160. }
  161. logs.Error("not exist the migrationMap name:" + name)
  162. time.Sleep(2 * time.Second)
  163. return errors.New("not exist the migrationMap name:" + name)
  164. }
  165. // Reset reset all migration
  166. // run all migration's down function
  167. func Reset() error {
  168. sm := sortMap(migrationMap)
  169. i := 0
  170. for j := len(sm) - 1; j >= 0; j-- {
  171. v := sm[j]
  172. if isRollBack(v.name) {
  173. logs.Info("skip the", v.name)
  174. time.Sleep(1 * time.Second)
  175. continue
  176. }
  177. logs.Info("start reset:", v.name)
  178. v.m.Reset()
  179. v.m.Down()
  180. err := v.m.Exec(v.name, "down")
  181. if err != nil {
  182. logs.Error("execute error:", err)
  183. time.Sleep(2 * time.Second)
  184. return err
  185. }
  186. i++
  187. logs.Info("end reset:", v.name)
  188. }
  189. logs.Info("total success reset:", i, " migration")
  190. time.Sleep(2 * time.Second)
  191. return nil
  192. }
  193. // Refresh first Reset, then Upgrade
  194. func Refresh() error {
  195. err := Reset()
  196. if err != nil {
  197. logs.Error("execute error:", err)
  198. time.Sleep(2 * time.Second)
  199. return err
  200. }
  201. err = Upgrade(0)
  202. return err
  203. }
  204. type dataSlice []data
  205. type data struct {
  206. created int64
  207. name string
  208. m Migrationer
  209. }
  210. // Len is part of sort.Interface.
  211. func (d dataSlice) Len() int {
  212. return len(d)
  213. }
  214. // Swap is part of sort.Interface.
  215. func (d dataSlice) Swap(i, j int) {
  216. d[i], d[j] = d[j], d[i]
  217. }
  218. // Less is part of sort.Interface. We use count as the value to sort by
  219. func (d dataSlice) Less(i, j int) bool {
  220. return d[i].created < d[j].created
  221. }
  222. func sortMap(m map[string]Migrationer) dataSlice {
  223. s := make(dataSlice, 0, len(m))
  224. for k, v := range m {
  225. d := data{}
  226. d.created = v.GetCreated()
  227. d.name = k
  228. d.m = v
  229. s = append(s, d)
  230. }
  231. sort.Sort(s)
  232. return s
  233. }
  234. func isRollBack(name string) bool {
  235. o := orm.NewOrm()
  236. var maps []orm.Params
  237. num, err := o.Raw("select * from migrations where `name` = ? order by id_migration desc", name).Values(&maps)
  238. if err != nil {
  239. logs.Info("get name has error", err)
  240. return false
  241. }
  242. if num <= 0 {
  243. return false
  244. }
  245. if maps[0]["status"] == "rollback" {
  246. return true
  247. }
  248. return false
  249. }