2014-08-07 08:30:28 +00:00
// Beego (http://beego.me/)
//
// @description beego is an open-source, high-performance web framework for the Go programming language.
//
// @link http://github.com/astaxie/beego for the canonical source repository
//
// @license http://github.com/astaxie/beego/blob/master/LICENSE
//
// @authors astaxie
2014-08-12 07:50:28 +00:00
//
// CREATE TABLE `migrations` (
// `id_migration` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'surrogate key',
// `name` varchar(255) DEFAULT NULL COMMENT 'migration name, unique',
// `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'date migrated or rolled back',
// `statements` longtext COMMENT 'SQL statements for this migration',
// `rollback_statements` longtext,
// `status` enum('update','rollback') DEFAULT NULL COMMENT 'update indicates it is a normal migration while rollback means this migration is rolled back',
2014-08-13 08:42:16 +00:00
// PRIMARY KEY (`id_migration`)
2014-08-12 07:50:28 +00:00
// ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
2014-08-07 08:30:28 +00:00
package migration
import (
"errors"
"sort"
2014-08-12 07:49:30 +00:00
"strings"
2014-08-07 08:30:28 +00:00
"time"
"github.com/astaxie/beego"
"github.com/astaxie/beego/orm"
)
// const the data format for the bee generate migration datatype
2014-08-13 03:16:19 +00:00
const (
M_DATE_FORMAT = "20060102_150405"
M_DB_DATE_FORMAT = "2006-01-02 15:04:05"
)
2014-08-07 08:30:28 +00:00
// Migrationer is an interface for all Migration struct
type Migrationer interface {
Up ( )
Down ( )
2014-08-12 07:49:30 +00:00
Exec ( name , status string ) error
2014-08-07 08:30:28 +00:00
GetCreated ( ) int64
}
2014-08-14 02:19:55 +00:00
var (
migrationMap map [ string ] Migrationer
)
2014-08-07 08:30:28 +00:00
func init ( ) {
migrationMap = make ( map [ string ] Migrationer )
}
// the basic type which will implement the basic type
type Migration struct {
sqls [ ] string
Created string
}
// implement in the Inheritance struct for upgrade
func ( m * Migration ) Up ( ) {
}
// implement in the Inheritance struct for down
func ( m * Migration ) Down ( ) {
}
// add sql want to execute
func ( m * Migration ) Sql ( sql string ) {
m . sqls = append ( m . sqls , sql )
}
// execute the sql already add in the sql
2014-08-12 07:49:30 +00:00
func ( m * Migration ) Exec ( name , status string ) error {
2014-08-07 08:30:28 +00:00
o := orm . NewOrm ( )
for _ , s := range m . sqls {
beego . Info ( "exec sql:" , s )
r := o . Raw ( s )
_ , err := r . Exec ( )
if err != nil {
return err
}
}
2014-08-12 07:49:30 +00:00
return m . addOrUpdateRecord ( name , status )
}
2014-08-13 02:43:05 +00:00
func ( m * Migration ) addOrUpdateRecord ( name , status string ) error {
2014-08-12 07:49:30 +00:00
o := orm . NewOrm ( )
if status == "down" {
status = "rollback"
2014-08-13 02:43:05 +00:00
p , err := o . Raw ( "update migrations set status = ?, rollback_statements = ? where name = ?" ) . Prepare ( )
2014-08-12 07:49:30 +00:00
if err != nil {
return nil
}
_ , err = p . Exec ( status , strings . Join ( m . sqls , "; " ) , name )
return err
} else {
status = "update"
2014-08-13 02:43:05 +00:00
p , err := o . Raw ( "insert into migrations(`name`, `created_at`, `statements`, `status`) values(?,?,?,?)" ) . Prepare ( )
2014-08-12 07:49:30 +00:00
if err != nil {
return err
}
2014-08-13 08:09:13 +00:00
t , err := time . Parse ( M_DATE_FORMAT , m . Created )
2014-08-13 03:16:19 +00:00
if err != nil {
return err
}
_ , err = p . Exec ( name , t . Format ( M_DB_DATE_FORMAT ) , strings . Join ( m . sqls , "; " ) , status )
2014-08-12 07:49:30 +00:00
return err
}
2014-08-07 08:30:28 +00:00
}
// get the unixtime from the Created
func ( m * Migration ) GetCreated ( ) int64 {
t , err := time . Parse ( M_DATE_FORMAT , m . Created )
if err != nil {
return 0
}
return t . Unix ( )
}
// register the Migration in the map
func Register ( name string , m Migrationer ) error {
if _ , ok := migrationMap [ name ] ; ok {
return errors . New ( "already exist name:" + name )
}
migrationMap [ name ] = m
return nil
}
// upgrate the migration from lasttime
func Upgrade ( lasttime int64 ) error {
sm := sortMap ( migrationMap )
i := 0
for _ , v := range sm {
if v . created > lasttime {
beego . Info ( "start upgrade" , v . name )
v . m . Up ( )
2014-08-12 07:49:30 +00:00
err := v . m . Exec ( v . name , "up" )
2014-08-07 08:30:28 +00:00
if err != nil {
2014-08-12 08:35:59 +00:00
beego . Error ( "execute error:" , err )
2014-08-13 06:50:32 +00:00
time . Sleep ( 2 * time . Second )
2014-08-07 08:30:28 +00:00
return err
}
beego . Info ( "end upgrade:" , v . name )
i ++
}
}
beego . Info ( "total success upgrade:" , i , " migration" )
2014-08-14 03:39:59 +00:00
time . Sleep ( 2 * time . Second )
2014-08-07 08:30:28 +00:00
return nil
}
//rollback the migration by the name
func Rollback ( name string ) error {
if v , ok := migrationMap [ name ] ; ok {
beego . Info ( "start rollback" )
v . Down ( )
2014-08-12 07:49:30 +00:00
err := v . Exec ( name , "down" )
2014-08-07 08:30:28 +00:00
if err != nil {
2014-08-12 08:35:59 +00:00
beego . Error ( "execute error:" , err )
2014-08-13 06:50:32 +00:00
time . Sleep ( 2 * time . Second )
2014-08-07 08:30:28 +00:00
return err
}
beego . Info ( "end rollback" )
2014-08-14 03:39:59 +00:00
time . Sleep ( 2 * time . Second )
2014-08-07 08:30:28 +00:00
return nil
} else {
return errors . New ( "not exist the migrationMap name:" + name )
}
}
// reset all migration
// run all migration's down function
func Reset ( ) error {
2014-08-14 03:44:10 +00:00
sm := sortMap ( migrationMap )
2014-08-07 08:30:28 +00:00
i := 0
2014-08-14 03:54:15 +00:00
for j := len ( sm ) - 1 ; j >= 0 ; j -- {
v := sm [ j ]
2014-08-14 03:44:10 +00:00
if isRollBack ( v . name ) {
beego . Info ( "skip the" , v . name )
2014-08-14 03:39:59 +00:00
time . Sleep ( 1 * time . Second )
2014-08-14 02:19:55 +00:00
continue
}
2014-08-14 03:44:10 +00:00
beego . Info ( "start reset:" , v . name )
v . m . Down ( )
err := v . m . Exec ( v . name , "down" )
2014-08-07 08:30:28 +00:00
if err != nil {
2014-08-12 08:35:59 +00:00
beego . Error ( "execute error:" , err )
2014-08-13 06:50:32 +00:00
time . Sleep ( 2 * time . Second )
2014-08-07 08:30:28 +00:00
return err
}
2014-08-14 03:44:10 +00:00
i ++
beego . Info ( "end reset:" , v . name )
2014-08-07 08:30:28 +00:00
}
beego . Info ( "total success reset:" , i , " migration" )
2014-08-14 03:39:59 +00:00
time . Sleep ( 2 * time . Second )
2014-08-07 08:30:28 +00:00
return nil
}
// first Reset, then Upgrade
func Refresh ( ) error {
err := Reset ( )
if err != nil {
2014-08-12 08:35:59 +00:00
beego . Error ( "execute error:" , err )
2014-08-13 06:50:32 +00:00
time . Sleep ( 2 * time . Second )
2014-08-07 08:30:28 +00:00
return err
}
2014-08-14 03:54:15 +00:00
err = Upgrade ( 0 )
return err
2014-08-07 08:30:28 +00:00
}
type dataSlice [ ] data
type data struct {
created int64
name string
m Migrationer
}
// Len is part of sort.Interface.
func ( d dataSlice ) Len ( ) int {
return len ( d )
}
// Swap is part of sort.Interface.
func ( d dataSlice ) Swap ( i , j int ) {
d [ i ] , d [ j ] = d [ j ] , d [ i ]
}
// Less is part of sort.Interface. We use count as the value to sort by
func ( d dataSlice ) Less ( i , j int ) bool {
return d [ i ] . created < d [ j ] . created
}
func sortMap ( m map [ string ] Migrationer ) dataSlice {
s := make ( dataSlice , 0 , len ( m ) )
for k , v := range m {
d := data { }
d . created = v . GetCreated ( )
d . name = k
d . m = v
s = append ( s , d )
}
sort . Sort ( s )
return s
}
2014-08-14 02:19:55 +00:00
2014-08-14 02:56:49 +00:00
func isRollBack ( name string ) bool {
o := orm . NewOrm ( )
var maps [ ] orm . Params
2014-08-14 03:39:59 +00:00
num , err := o . Raw ( "select * from migrations where `name` = ? order by id_migration desc" , name ) . Values ( & maps )
2014-08-14 02:56:49 +00:00
if err != nil {
beego . Info ( "get name has error" , err )
return false
}
if num <= 0 {
return false
}
if maps [ 0 ] [ "status" ] == "rollback" {
return true
2014-08-14 02:19:55 +00:00
}
return false
}