1
0
mirror of https://github.com/astaxie/beego.git synced 2024-11-16 20:30:55 +00:00
Beego/migration/migration.go

331 lines
7.7 KiB
Go
Raw Normal View History

2014-08-18 08:41:43 +00:00
// Copyright 2014 beego Author. All Rights Reserved.
2014-08-07 08:30:28 +00:00
//
2014-08-18 08:41:43 +00:00
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
2014-08-07 08:30:28 +00:00
//
2014-08-18 08:41:43 +00:00
// http://www.apache.org/licenses/LICENSE-2.0
2014-08-07 08:30:28 +00:00
//
2014-08-18 08:41:43 +00:00
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
2015-09-11 15:16:05 +00:00
// Package migration is used for migration
2014-08-07 08:30:28 +00:00
//
2014-08-18 08:41:43 +00:00
// The table structure is as follow:
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',
// 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"
2016-03-25 02:56:15 +00:00
"github.com/astaxie/beego/logs"
2014-08-07 08:30:28 +00:00
"github.com/astaxie/beego/orm"
)
// const the data format for the bee generate migration datatype
const (
2015-09-11 15:16:05 +00:00
DateFormat = "20060102_150405"
DBDateFormat = "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-14 05:37:48 +00:00
Reset()
2014-08-12 07:49:30 +00:00
Exec(name, status string) error
2014-08-07 08:30:28 +00:00
GetCreated() int64
}
//Migration defines the migrations by either SQL or DDL
type Migration struct {
sqls []string
Created string
TableName string
Engine string
Charset string
ModifyType string
Columns []*Column
Indexes []*Index
Primary []*Column
Uniques []*Unique
Foreigns []*Foreign
Renames []*RenameColumn
RemoveColumns []*Column
RemoveIndexes []*Index
RemoveUniques []*Unique
RemoveForeigns []*Foreign
}
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)
}
2015-09-11 15:16:05 +00:00
// Up implement in the Inheritance struct for upgrade
2014-08-07 08:30:28 +00:00
func (m *Migration) Up() {
switch m.ModifyType {
case "reverse":
m.ModifyType = "alter"
case "delete":
m.ModifyType = "create"
}
m.sqls = append(m.sqls, m.GetSQL())
2014-08-07 08:30:28 +00:00
}
2015-09-11 15:16:05 +00:00
// Down implement in the Inheritance struct for down
2014-08-07 08:30:28 +00:00
func (m *Migration) Down() {
switch m.ModifyType {
case "alter":
m.ModifyType = "reverse"
case "create":
m.ModifyType = "delete"
}
m.sqls = append(m.sqls, m.GetSQL())
2014-08-07 08:30:28 +00:00
}
//Migrate adds the SQL to the execution list
func (m *Migration) Migrate(migrationType string) {
m.ModifyType = migrationType
m.sqls = append(m.sqls, m.GetSQL())
2014-08-07 08:30:28 +00:00
}
2015-09-11 15:16:05 +00:00
// SQL add sql want to execute
func (m *Migration) SQL(sql string) {
2014-08-07 08:30:28 +00:00
m.sqls = append(m.sqls, sql)
}
2014-08-14 05:37:48 +00:00
// Reset the sqls
func (m *Migration) Reset() {
m.sqls = make([]string, 0)
}
2015-09-11 15:16:05 +00:00
// Exec 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 {
2016-03-25 02:56:15 +00:00
logs.Info("exec sql:", s)
2014-08-07 08:30:28 +00:00
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)
}
func (m *Migration) addOrUpdateRecord(name, status string) error {
2014-08-12 07:49:30 +00:00
o := orm.NewOrm()
if status == "down" {
status = "rollback"
p, err := o.Raw("update migrations set status = ?, rollback_statements = ?, created_at = ? where name = ?").Prepare()
2014-08-12 07:49:30 +00:00
if err != nil {
return nil
}
2015-09-11 15:16:05 +00:00
_, err = p.Exec(status, strings.Join(m.sqls, "; "), time.Now().Format(DBDateFormat), name)
2014-08-12 07:49:30 +00:00
return err
2015-09-11 15:16:05 +00:00
}
status = "update"
p, err := o.Raw("insert into migrations(name, created_at, statements, status) values(?,?,?,?)").Prepare()
2015-09-11 15:16:05 +00:00
if err != nil {
2014-08-12 07:49:30 +00:00
return err
}
2015-09-11 15:16:05 +00:00
_, err = p.Exec(name, time.Now().Format(DBDateFormat), strings.Join(m.sqls, "; "), status)
return err
2014-08-07 08:30:28 +00:00
}
2015-09-11 15:16:05 +00:00
// GetCreated get the unixtime from the Created
2014-08-07 08:30:28 +00:00
func (m *Migration) GetCreated() int64 {
2015-09-11 15:16:05 +00:00
t, err := time.Parse(DateFormat, m.Created)
2014-08-07 08:30:28 +00:00
if err != nil {
return 0
}
return t.Unix()
}
2015-09-11 15:16:05 +00:00
// Register register the Migration in the map
2014-08-07 08:30:28 +00:00
func Register(name string, m Migrationer) error {
if _, ok := migrationMap[name]; ok {
return errors.New("already exist name:" + name)
}
migrationMap[name] = m
return nil
}
2017-10-17 09:27:03 +00:00
// Upgrade upgrade the migration from lasttime
2014-08-07 08:30:28 +00:00
func Upgrade(lasttime int64) error {
sm := sortMap(migrationMap)
i := 0
migs, _ := getAllMigrations()
2014-08-07 08:30:28 +00:00
for _, v := range sm {
if _, ok := migs[v.name]; !ok {
2016-03-25 02:56:15 +00:00
logs.Info("start upgrade", v.name)
2014-08-14 05:37:48 +00:00
v.m.Reset()
2014-08-07 08:30:28 +00:00
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 {
2016-03-25 02:56:15 +00:00
logs.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
}
2016-03-25 02:56:15 +00:00
logs.Info("end upgrade:", v.name)
2014-08-07 08:30:28 +00:00
i++
}
}
2016-03-25 02:56:15 +00:00
logs.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
}
2015-09-11 15:16:05 +00:00
// Rollback rollback the migration by the name
2014-08-07 08:30:28 +00:00
func Rollback(name string) error {
if v, ok := migrationMap[name]; ok {
2016-03-25 02:56:15 +00:00
logs.Info("start rollback")
2014-08-14 05:37:48 +00:00
v.Reset()
2014-08-07 08:30:28 +00:00
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 {
2016-03-25 02:56:15 +00:00
logs.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
}
2016-03-25 02:56:15 +00:00
logs.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
}
2016-03-25 02:56:15 +00:00
logs.Error("not exist the migrationMap name:" + name)
2015-09-11 15:16:05 +00:00
time.Sleep(2 * time.Second)
return errors.New("not exist the migrationMap name:" + name)
2014-08-07 08:30:28 +00:00
}
2015-09-11 15:16:05 +00:00
// Reset reset all migration
2014-08-07 08:30:28 +00:00
// 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) {
2016-03-25 02:56:15 +00:00
logs.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
}
2016-03-25 02:56:15 +00:00
logs.Info("start reset:", v.name)
2014-08-14 05:37:48 +00:00
v.m.Reset()
2014-08-14 03:44:10 +00:00
v.m.Down()
err := v.m.Exec(v.name, "down")
2014-08-07 08:30:28 +00:00
if err != nil {
2016-03-25 02:56:15 +00:00
logs.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++
2016-03-25 02:56:15 +00:00
logs.Info("end reset:", v.name)
2014-08-07 08:30:28 +00:00
}
2016-03-25 02:56:15 +00:00
logs.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
}
2015-09-11 15:16:05 +00:00
// Refresh first Reset, then Upgrade
2014-08-07 08:30:28 +00:00
func Refresh() error {
err := Reset()
if err != nil {
2016-03-25 02:56:15 +00:00
logs.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 {
2016-03-25 02:56:15 +00:00
logs.Info("get name has error", err)
2014-08-14 02:56:49 +00:00
return false
}
if num <= 0 {
return false
}
if maps[0]["status"] == "rollback" {
return true
2014-08-14 02:19:55 +00:00
}
return false
}
func getAllMigrations() (map[string]string, error) {
o := orm.NewOrm()
var maps []orm.Params
migs := make(map[string]string)
num, err := o.Raw("select * from migrations order by id_migration desc").Values(&maps)
if err != nil {
logs.Info("get name has error", err)
return migs, err
}
if num > 0 {
for _, v := range maps {
name := v["name"].(string)
migs[name] = v["status"].(string)
}
}
return migs, nil
}