mirror of
https://github.com/astaxie/beego.git
synced 2025-06-11 14:10:39 +00:00
Move package
This commit is contained in:
395
pkg/migration/ddl.go
Normal file
395
pkg/migration/ddl.go
Normal file
@ -0,0 +1,395 @@
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// 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.
|
||||
|
||||
package migration
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/astaxie/beego/logs"
|
||||
)
|
||||
|
||||
// Index struct defines the structure of Index Columns
|
||||
type Index struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
// Unique struct defines a single unique key combination
|
||||
type Unique struct {
|
||||
Definition string
|
||||
Columns []*Column
|
||||
}
|
||||
|
||||
//Column struct defines a single column of a table
|
||||
type Column struct {
|
||||
Name string
|
||||
Inc string
|
||||
Null string
|
||||
Default string
|
||||
Unsign string
|
||||
DataType string
|
||||
remove bool
|
||||
Modify bool
|
||||
}
|
||||
|
||||
// Foreign struct defines a single foreign relationship
|
||||
type Foreign struct {
|
||||
ForeignTable string
|
||||
ForeignColumn string
|
||||
OnDelete string
|
||||
OnUpdate string
|
||||
Column
|
||||
}
|
||||
|
||||
// RenameColumn struct allows renaming of columns
|
||||
type RenameColumn struct {
|
||||
OldName string
|
||||
OldNull string
|
||||
OldDefault string
|
||||
OldUnsign string
|
||||
OldDataType string
|
||||
NewName string
|
||||
Column
|
||||
}
|
||||
|
||||
// CreateTable creates the table on system
|
||||
func (m *Migration) CreateTable(tablename, engine, charset string, p ...func()) {
|
||||
m.TableName = tablename
|
||||
m.Engine = engine
|
||||
m.Charset = charset
|
||||
m.ModifyType = "create"
|
||||
}
|
||||
|
||||
// AlterTable set the ModifyType to alter
|
||||
func (m *Migration) AlterTable(tablename string) {
|
||||
m.TableName = tablename
|
||||
m.ModifyType = "alter"
|
||||
}
|
||||
|
||||
// NewCol creates a new standard column and attaches it to m struct
|
||||
func (m *Migration) NewCol(name string) *Column {
|
||||
col := &Column{Name: name}
|
||||
m.AddColumns(col)
|
||||
return col
|
||||
}
|
||||
|
||||
//PriCol creates a new primary column and attaches it to m struct
|
||||
func (m *Migration) PriCol(name string) *Column {
|
||||
col := &Column{Name: name}
|
||||
m.AddColumns(col)
|
||||
m.AddPrimary(col)
|
||||
return col
|
||||
}
|
||||
|
||||
//UniCol creates / appends columns to specified unique key and attaches it to m struct
|
||||
func (m *Migration) UniCol(uni, name string) *Column {
|
||||
col := &Column{Name: name}
|
||||
m.AddColumns(col)
|
||||
|
||||
uniqueOriginal := &Unique{}
|
||||
|
||||
for _, unique := range m.Uniques {
|
||||
if unique.Definition == uni {
|
||||
unique.AddColumnsToUnique(col)
|
||||
uniqueOriginal = unique
|
||||
}
|
||||
}
|
||||
if uniqueOriginal.Definition == "" {
|
||||
unique := &Unique{Definition: uni}
|
||||
unique.AddColumnsToUnique(col)
|
||||
m.AddUnique(unique)
|
||||
}
|
||||
|
||||
return col
|
||||
}
|
||||
|
||||
//ForeignCol creates a new foreign column and returns the instance of column
|
||||
func (m *Migration) ForeignCol(colname, foreigncol, foreigntable string) (foreign *Foreign) {
|
||||
|
||||
foreign = &Foreign{ForeignColumn: foreigncol, ForeignTable: foreigntable}
|
||||
foreign.Name = colname
|
||||
m.AddForeign(foreign)
|
||||
return foreign
|
||||
}
|
||||
|
||||
//SetOnDelete sets the on delete of foreign
|
||||
func (foreign *Foreign) SetOnDelete(del string) *Foreign {
|
||||
foreign.OnDelete = "ON DELETE" + del
|
||||
return foreign
|
||||
}
|
||||
|
||||
//SetOnUpdate sets the on update of foreign
|
||||
func (foreign *Foreign) SetOnUpdate(update string) *Foreign {
|
||||
foreign.OnUpdate = "ON UPDATE" + update
|
||||
return foreign
|
||||
}
|
||||
|
||||
//Remove marks the columns to be removed.
|
||||
//it allows reverse m to create the column.
|
||||
func (c *Column) Remove() {
|
||||
c.remove = true
|
||||
}
|
||||
|
||||
//SetAuto enables auto_increment of column (can be used once)
|
||||
func (c *Column) SetAuto(inc bool) *Column {
|
||||
if inc {
|
||||
c.Inc = "auto_increment"
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
//SetNullable sets the column to be null
|
||||
func (c *Column) SetNullable(null bool) *Column {
|
||||
if null {
|
||||
c.Null = ""
|
||||
|
||||
} else {
|
||||
c.Null = "NOT NULL"
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
//SetDefault sets the default value, prepend with "DEFAULT "
|
||||
func (c *Column) SetDefault(def string) *Column {
|
||||
c.Default = "DEFAULT " + def
|
||||
return c
|
||||
}
|
||||
|
||||
//SetUnsigned sets the column to be unsigned int
|
||||
func (c *Column) SetUnsigned(unsign bool) *Column {
|
||||
if unsign {
|
||||
c.Unsign = "UNSIGNED"
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
//SetDataType sets the dataType of the column
|
||||
func (c *Column) SetDataType(dataType string) *Column {
|
||||
c.DataType = dataType
|
||||
return c
|
||||
}
|
||||
|
||||
//SetOldNullable allows reverting to previous nullable on reverse ms
|
||||
func (c *RenameColumn) SetOldNullable(null bool) *RenameColumn {
|
||||
if null {
|
||||
c.OldNull = ""
|
||||
|
||||
} else {
|
||||
c.OldNull = "NOT NULL"
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
//SetOldDefault allows reverting to previous default on reverse ms
|
||||
func (c *RenameColumn) SetOldDefault(def string) *RenameColumn {
|
||||
c.OldDefault = def
|
||||
return c
|
||||
}
|
||||
|
||||
//SetOldUnsigned allows reverting to previous unsgined on reverse ms
|
||||
func (c *RenameColumn) SetOldUnsigned(unsign bool) *RenameColumn {
|
||||
if unsign {
|
||||
c.OldUnsign = "UNSIGNED"
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
//SetOldDataType allows reverting to previous datatype on reverse ms
|
||||
func (c *RenameColumn) SetOldDataType(dataType string) *RenameColumn {
|
||||
c.OldDataType = dataType
|
||||
return c
|
||||
}
|
||||
|
||||
//SetPrimary adds the columns to the primary key (can only be used any number of times in only one m)
|
||||
func (c *Column) SetPrimary(m *Migration) *Column {
|
||||
m.Primary = append(m.Primary, c)
|
||||
return c
|
||||
}
|
||||
|
||||
//AddColumnsToUnique adds the columns to Unique Struct
|
||||
func (unique *Unique) AddColumnsToUnique(columns ...*Column) *Unique {
|
||||
|
||||
unique.Columns = append(unique.Columns, columns...)
|
||||
|
||||
return unique
|
||||
}
|
||||
|
||||
//AddColumns adds columns to m struct
|
||||
func (m *Migration) AddColumns(columns ...*Column) *Migration {
|
||||
|
||||
m.Columns = append(m.Columns, columns...)
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
//AddPrimary adds the column to primary in m struct
|
||||
func (m *Migration) AddPrimary(primary *Column) *Migration {
|
||||
m.Primary = append(m.Primary, primary)
|
||||
return m
|
||||
}
|
||||
|
||||
//AddUnique adds the column to unique in m struct
|
||||
func (m *Migration) AddUnique(unique *Unique) *Migration {
|
||||
m.Uniques = append(m.Uniques, unique)
|
||||
return m
|
||||
}
|
||||
|
||||
//AddForeign adds the column to foreign in m struct
|
||||
func (m *Migration) AddForeign(foreign *Foreign) *Migration {
|
||||
m.Foreigns = append(m.Foreigns, foreign)
|
||||
return m
|
||||
}
|
||||
|
||||
//AddIndex adds the column to index in m struct
|
||||
func (m *Migration) AddIndex(index *Index) *Migration {
|
||||
m.Indexes = append(m.Indexes, index)
|
||||
return m
|
||||
}
|
||||
|
||||
//RenameColumn allows renaming of columns
|
||||
func (m *Migration) RenameColumn(from, to string) *RenameColumn {
|
||||
rename := &RenameColumn{OldName: from, NewName: to}
|
||||
m.Renames = append(m.Renames, rename)
|
||||
return rename
|
||||
}
|
||||
|
||||
//GetSQL returns the generated sql depending on ModifyType
|
||||
func (m *Migration) GetSQL() (sql string) {
|
||||
sql = ""
|
||||
switch m.ModifyType {
|
||||
case "create":
|
||||
{
|
||||
sql += fmt.Sprintf("CREATE TABLE `%s` (", m.TableName)
|
||||
for index, column := range m.Columns {
|
||||
sql += fmt.Sprintf("\n `%s` %s %s %s %s %s", column.Name, column.DataType, column.Unsign, column.Null, column.Inc, column.Default)
|
||||
if len(m.Columns) > index+1 {
|
||||
sql += ","
|
||||
}
|
||||
}
|
||||
|
||||
if len(m.Primary) > 0 {
|
||||
sql += fmt.Sprintf(",\n PRIMARY KEY( ")
|
||||
}
|
||||
for index, column := range m.Primary {
|
||||
sql += fmt.Sprintf(" `%s`", column.Name)
|
||||
if len(m.Primary) > index+1 {
|
||||
sql += ","
|
||||
}
|
||||
|
||||
}
|
||||
if len(m.Primary) > 0 {
|
||||
sql += fmt.Sprintf(")")
|
||||
}
|
||||
|
||||
for _, unique := range m.Uniques {
|
||||
sql += fmt.Sprintf(",\n UNIQUE KEY `%s`( ", unique.Definition)
|
||||
for index, column := range unique.Columns {
|
||||
sql += fmt.Sprintf(" `%s`", column.Name)
|
||||
if len(unique.Columns) > index+1 {
|
||||
sql += ","
|
||||
}
|
||||
}
|
||||
sql += fmt.Sprintf(")")
|
||||
}
|
||||
for _, foreign := range m.Foreigns {
|
||||
sql += fmt.Sprintf(",\n `%s` %s %s %s %s %s", foreign.Name, foreign.DataType, foreign.Unsign, foreign.Null, foreign.Inc, foreign.Default)
|
||||
sql += fmt.Sprintf(",\n KEY `%s_%s_foreign`(`%s`),", m.TableName, foreign.Column.Name, foreign.Column.Name)
|
||||
sql += fmt.Sprintf("\n CONSTRAINT `%s_%s_foreign` FOREIGN KEY (`%s`) REFERENCES `%s` (`%s`) %s %s", m.TableName, foreign.Column.Name, foreign.Column.Name, foreign.ForeignTable, foreign.ForeignColumn, foreign.OnDelete, foreign.OnUpdate)
|
||||
|
||||
}
|
||||
sql += fmt.Sprintf(")ENGINE=%s DEFAULT CHARSET=%s;", m.Engine, m.Charset)
|
||||
break
|
||||
}
|
||||
case "alter":
|
||||
{
|
||||
sql += fmt.Sprintf("ALTER TABLE `%s` ", m.TableName)
|
||||
for index, column := range m.Columns {
|
||||
if !column.remove {
|
||||
logs.Info("col")
|
||||
sql += fmt.Sprintf("\n ADD `%s` %s %s %s %s %s", column.Name, column.DataType, column.Unsign, column.Null, column.Inc, column.Default)
|
||||
} else {
|
||||
sql += fmt.Sprintf("\n DROP COLUMN `%s`", column.Name)
|
||||
}
|
||||
|
||||
if len(m.Columns) > index+1 {
|
||||
sql += ","
|
||||
}
|
||||
}
|
||||
for index, column := range m.Renames {
|
||||
sql += fmt.Sprintf("CHANGE COLUMN `%s` `%s` %s %s %s %s %s", column.OldName, column.NewName, column.DataType, column.Unsign, column.Null, column.Inc, column.Default)
|
||||
if len(m.Renames) > index+1 {
|
||||
sql += ","
|
||||
}
|
||||
}
|
||||
|
||||
for index, foreign := range m.Foreigns {
|
||||
sql += fmt.Sprintf("ADD `%s` %s %s %s %s %s", foreign.Name, foreign.DataType, foreign.Unsign, foreign.Null, foreign.Inc, foreign.Default)
|
||||
sql += fmt.Sprintf(",\n ADD KEY `%s_%s_foreign`(`%s`)", m.TableName, foreign.Column.Name, foreign.Column.Name)
|
||||
sql += fmt.Sprintf(",\n ADD CONSTRAINT `%s_%s_foreign` FOREIGN KEY (`%s`) REFERENCES `%s` (`%s`) %s %s", m.TableName, foreign.Column.Name, foreign.Column.Name, foreign.ForeignTable, foreign.ForeignColumn, foreign.OnDelete, foreign.OnUpdate)
|
||||
if len(m.Foreigns) > index+1 {
|
||||
sql += ","
|
||||
}
|
||||
}
|
||||
sql += ";"
|
||||
|
||||
break
|
||||
}
|
||||
case "reverse":
|
||||
{
|
||||
|
||||
sql += fmt.Sprintf("ALTER TABLE `%s`", m.TableName)
|
||||
for index, column := range m.Columns {
|
||||
if column.remove {
|
||||
sql += fmt.Sprintf("\n ADD `%s` %s %s %s %s %s", column.Name, column.DataType, column.Unsign, column.Null, column.Inc, column.Default)
|
||||
} else {
|
||||
sql += fmt.Sprintf("\n DROP COLUMN `%s`", column.Name)
|
||||
}
|
||||
if len(m.Columns) > index+1 {
|
||||
sql += ","
|
||||
}
|
||||
}
|
||||
|
||||
if len(m.Primary) > 0 {
|
||||
sql += fmt.Sprintf("\n DROP PRIMARY KEY,")
|
||||
}
|
||||
|
||||
for index, unique := range m.Uniques {
|
||||
sql += fmt.Sprintf("\n DROP KEY `%s`", unique.Definition)
|
||||
if len(m.Uniques) > index+1 {
|
||||
sql += ","
|
||||
}
|
||||
|
||||
}
|
||||
for index, column := range m.Renames {
|
||||
sql += fmt.Sprintf("\n CHANGE COLUMN `%s` `%s` %s %s %s %s", column.NewName, column.OldName, column.OldDataType, column.OldUnsign, column.OldNull, column.OldDefault)
|
||||
if len(m.Renames) > index+1 {
|
||||
sql += ","
|
||||
}
|
||||
}
|
||||
|
||||
for _, foreign := range m.Foreigns {
|
||||
sql += fmt.Sprintf("\n DROP KEY `%s_%s_foreign`", m.TableName, foreign.Column.Name)
|
||||
sql += fmt.Sprintf(",\n DROP FOREIGN KEY `%s_%s_foreign`", m.TableName, foreign.Column.Name)
|
||||
sql += fmt.Sprintf(",\n DROP COLUMN `%s`", foreign.Name)
|
||||
}
|
||||
sql += ";"
|
||||
}
|
||||
case "delete":
|
||||
{
|
||||
sql += fmt.Sprintf("DROP TABLE IF EXISTS `%s`;", m.TableName)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
32
pkg/migration/doc.go
Normal file
32
pkg/migration/doc.go
Normal file
@ -0,0 +1,32 @@
|
||||
// Package migration enables you to generate migrations back and forth. It generates both migrations.
|
||||
//
|
||||
// //Creates a table
|
||||
// m.CreateTable("tablename","InnoDB","utf8");
|
||||
//
|
||||
// //Alter a table
|
||||
// m.AlterTable("tablename")
|
||||
//
|
||||
// Standard Column Methods
|
||||
// * SetDataType
|
||||
// * SetNullable
|
||||
// * SetDefault
|
||||
// * SetUnsigned (use only on integer types unless produces error)
|
||||
//
|
||||
// //Sets a primary column, multiple calls allowed, standard column methods available
|
||||
// m.PriCol("id").SetAuto(true).SetNullable(false).SetDataType("INT(10)").SetUnsigned(true)
|
||||
//
|
||||
// //UniCol Can be used multiple times, allows standard Column methods. Use same "index" string to add to same index
|
||||
// m.UniCol("index","column")
|
||||
//
|
||||
// //Standard Column Initialisation, can call .Remove() after NewCol("") on alter to remove
|
||||
// m.NewCol("name").SetDataType("VARCHAR(255) COLLATE utf8_unicode_ci").SetNullable(false)
|
||||
// m.NewCol("value").SetDataType("DOUBLE(8,2)").SetNullable(false)
|
||||
//
|
||||
// //Rename Columns , only use with Alter table, doesn't works with Create, prefix standard column methods with "Old" to
|
||||
// //create a true reversible migration eg: SetOldDataType("DOUBLE(12,3)")
|
||||
// m.RenameColumn("from","to")...
|
||||
//
|
||||
// //Foreign Columns, single columns are only supported, SetOnDelete & SetOnUpdate are available, call appropriately.
|
||||
// //Supports standard column methods, automatic reverse.
|
||||
// m.ForeignCol("local_col","foreign_col","foreign_table")
|
||||
package migration
|
330
pkg/migration/migration.go
Normal file
330
pkg/migration/migration.go
Normal file
@ -0,0 +1,330 @@
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// 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.
|
||||
|
||||
// Package migration is used for migration
|
||||
//
|
||||
// The table structure is as follow:
|
||||
//
|
||||
// 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`)
|
||||
// ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
package migration
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/astaxie/beego/logs"
|
||||
"github.com/astaxie/beego/orm"
|
||||
)
|
||||
|
||||
// const the data format for the bee generate migration datatype
|
||||
const (
|
||||
DateFormat = "20060102_150405"
|
||||
DBDateFormat = "2006-01-02 15:04:05"
|
||||
)
|
||||
|
||||
// Migrationer is an interface for all Migration struct
|
||||
type Migrationer interface {
|
||||
Up()
|
||||
Down()
|
||||
Reset()
|
||||
Exec(name, status string) error
|
||||
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
|
||||
}
|
||||
|
||||
var (
|
||||
migrationMap map[string]Migrationer
|
||||
)
|
||||
|
||||
func init() {
|
||||
migrationMap = make(map[string]Migrationer)
|
||||
}
|
||||
|
||||
// Up implement in the Inheritance struct for upgrade
|
||||
func (m *Migration) Up() {
|
||||
|
||||
switch m.ModifyType {
|
||||
case "reverse":
|
||||
m.ModifyType = "alter"
|
||||
case "delete":
|
||||
m.ModifyType = "create"
|
||||
}
|
||||
m.sqls = append(m.sqls, m.GetSQL())
|
||||
}
|
||||
|
||||
// Down implement in the Inheritance struct for down
|
||||
func (m *Migration) Down() {
|
||||
|
||||
switch m.ModifyType {
|
||||
case "alter":
|
||||
m.ModifyType = "reverse"
|
||||
case "create":
|
||||
m.ModifyType = "delete"
|
||||
}
|
||||
m.sqls = append(m.sqls, m.GetSQL())
|
||||
}
|
||||
|
||||
//Migrate adds the SQL to the execution list
|
||||
func (m *Migration) Migrate(migrationType string) {
|
||||
m.ModifyType = migrationType
|
||||
m.sqls = append(m.sqls, m.GetSQL())
|
||||
}
|
||||
|
||||
// SQL add sql want to execute
|
||||
func (m *Migration) SQL(sql string) {
|
||||
m.sqls = append(m.sqls, sql)
|
||||
}
|
||||
|
||||
// Reset the sqls
|
||||
func (m *Migration) Reset() {
|
||||
m.sqls = make([]string, 0)
|
||||
}
|
||||
|
||||
// Exec execute the sql already add in the sql
|
||||
func (m *Migration) Exec(name, status string) error {
|
||||
o := orm.NewOrm()
|
||||
for _, s := range m.sqls {
|
||||
logs.Info("exec sql:", s)
|
||||
r := o.Raw(s)
|
||||
_, err := r.Exec()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return m.addOrUpdateRecord(name, status)
|
||||
}
|
||||
|
||||
func (m *Migration) addOrUpdateRecord(name, status string) error {
|
||||
o := orm.NewOrm()
|
||||
if status == "down" {
|
||||
status = "rollback"
|
||||
p, err := o.Raw("update migrations set status = ?, rollback_statements = ?, created_at = ? where name = ?").Prepare()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
_, err = p.Exec(status, strings.Join(m.sqls, "; "), time.Now().Format(DBDateFormat), name)
|
||||
return err
|
||||
}
|
||||
status = "update"
|
||||
p, err := o.Raw("insert into migrations(name, created_at, statements, status) values(?,?,?,?)").Prepare()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = p.Exec(name, time.Now().Format(DBDateFormat), strings.Join(m.sqls, "; "), status)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetCreated get the unixtime from the Created
|
||||
func (m *Migration) GetCreated() int64 {
|
||||
t, err := time.Parse(DateFormat, m.Created)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return t.Unix()
|
||||
}
|
||||
|
||||
// Register 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
|
||||
}
|
||||
|
||||
// Upgrade upgrade the migration from lasttime
|
||||
func Upgrade(lasttime int64) error {
|
||||
sm := sortMap(migrationMap)
|
||||
i := 0
|
||||
migs, _ := getAllMigrations()
|
||||
for _, v := range sm {
|
||||
if _, ok := migs[v.name]; !ok {
|
||||
logs.Info("start upgrade", v.name)
|
||||
v.m.Reset()
|
||||
v.m.Up()
|
||||
err := v.m.Exec(v.name, "up")
|
||||
if err != nil {
|
||||
logs.Error("execute error:", err)
|
||||
time.Sleep(2 * time.Second)
|
||||
return err
|
||||
}
|
||||
logs.Info("end upgrade:", v.name)
|
||||
i++
|
||||
}
|
||||
}
|
||||
logs.Info("total success upgrade:", i, " migration")
|
||||
time.Sleep(2 * time.Second)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Rollback rollback the migration by the name
|
||||
func Rollback(name string) error {
|
||||
if v, ok := migrationMap[name]; ok {
|
||||
logs.Info("start rollback")
|
||||
v.Reset()
|
||||
v.Down()
|
||||
err := v.Exec(name, "down")
|
||||
if err != nil {
|
||||
logs.Error("execute error:", err)
|
||||
time.Sleep(2 * time.Second)
|
||||
return err
|
||||
}
|
||||
logs.Info("end rollback")
|
||||
time.Sleep(2 * time.Second)
|
||||
return nil
|
||||
}
|
||||
logs.Error("not exist the migrationMap name:" + name)
|
||||
time.Sleep(2 * time.Second)
|
||||
return errors.New("not exist the migrationMap name:" + name)
|
||||
}
|
||||
|
||||
// Reset reset all migration
|
||||
// run all migration's down function
|
||||
func Reset() error {
|
||||
sm := sortMap(migrationMap)
|
||||
i := 0
|
||||
for j := len(sm) - 1; j >= 0; j-- {
|
||||
v := sm[j]
|
||||
if isRollBack(v.name) {
|
||||
logs.Info("skip the", v.name)
|
||||
time.Sleep(1 * time.Second)
|
||||
continue
|
||||
}
|
||||
logs.Info("start reset:", v.name)
|
||||
v.m.Reset()
|
||||
v.m.Down()
|
||||
err := v.m.Exec(v.name, "down")
|
||||
if err != nil {
|
||||
logs.Error("execute error:", err)
|
||||
time.Sleep(2 * time.Second)
|
||||
return err
|
||||
}
|
||||
i++
|
||||
logs.Info("end reset:", v.name)
|
||||
}
|
||||
logs.Info("total success reset:", i, " migration")
|
||||
time.Sleep(2 * time.Second)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Refresh first Reset, then Upgrade
|
||||
func Refresh() error {
|
||||
err := Reset()
|
||||
if err != nil {
|
||||
logs.Error("execute error:", err)
|
||||
time.Sleep(2 * time.Second)
|
||||
return err
|
||||
}
|
||||
err = Upgrade(0)
|
||||
return err
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func isRollBack(name string) bool {
|
||||
o := orm.NewOrm()
|
||||
var maps []orm.Params
|
||||
num, err := o.Raw("select * from migrations where `name` = ? order by id_migration desc", name).Values(&maps)
|
||||
if err != nil {
|
||||
logs.Info("get name has error", err)
|
||||
return false
|
||||
}
|
||||
if num <= 0 {
|
||||
return false
|
||||
}
|
||||
if maps[0]["status"] == "rollback" {
|
||||
return true
|
||||
}
|
||||
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
|
||||
}
|
Reference in New Issue
Block a user