DB Migrations (#67)
This commit is contained in:
38
vendor/src.techknowlogick.com/xormigrate/.drone.yml
generated
vendored
Normal file
38
vendor/src.techknowlogick.com/xormigrate/.drone.yml
generated
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
kind: pipeline
|
||||
name: default
|
||||
|
||||
workspace:
|
||||
base: /go
|
||||
path: src/src.techknowlogick.com/xormigrate
|
||||
|
||||
steps:
|
||||
- name: fetch-and-test
|
||||
image: golang:1.12
|
||||
environment:
|
||||
GO111MODULE: on
|
||||
commands:
|
||||
- go get ./...
|
||||
- go test -v -tags sqlite
|
||||
- go test -v -tags mysql
|
||||
- go test -v -tags postgresql
|
||||
- go test -v -tags sqlserver
|
||||
|
||||
services:
|
||||
- name: pgsql
|
||||
image: postgres:9.5
|
||||
environment:
|
||||
POSTGRES_DB: test
|
||||
POSTGRES_PASSWORD: postgres
|
||||
|
||||
- name: mysql
|
||||
image: mysql:5.7
|
||||
environment:
|
||||
MYSQL_DATABASE: test
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: yes
|
||||
|
||||
- name: mssql
|
||||
image: microsoft/mssql-server-linux:latest
|
||||
environment:
|
||||
ACCEPT_EULA: Y
|
||||
SA_PASSWORD: MwantsaSecurePassword1
|
||||
MSSQL_PID: Standard
|
4
vendor/src.techknowlogick.com/xormigrate/.env
generated
vendored
Normal file
4
vendor/src.techknowlogick.com/xormigrate/.env
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
SQLITE_CONN_STRING=file::memory:?cache=shared
|
||||
PG_CONN_STRING="user=postgres password=postgres host=pgsql dbname=test sslmode=disable"
|
||||
MYSQL_CONN_STRING="root:@(mysql)/test?multiStatements=true"
|
||||
SQLSERVER_CONN_STRING="server=mssql; database=master; user id=sa; password=MwantsaSecurePassword1; encrypt=disable"
|
19
vendor/src.techknowlogick.com/xormigrate/LICENSE
generated
vendored
Normal file
19
vendor/src.techknowlogick.com/xormigrate/LICENSE
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
Copyright (c) 2019 Matti Ranta (@techknowlogick)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
162
vendor/src.techknowlogick.com/xormigrate/README.md
generated
vendored
Normal file
162
vendor/src.techknowlogick.com/xormigrate/README.md
generated
vendored
Normal file
@ -0,0 +1,162 @@
|
||||
# Xormigrate
|
||||
[](https://cloud.drone.io/techknowlogick/xormigrate)
|
||||
[](https://goreportcard.com/report/src.techknowlogick.com/xormigrate)
|
||||
[](https://godoc.org/src.techknowlogick.com/xormigrate)
|
||||
|
||||
## Supported databases
|
||||
|
||||
It supports any of the databases Xorm supports:
|
||||
|
||||
- PostgreSQL
|
||||
- MySQL
|
||||
- SQLite
|
||||
- Microsoft SQL Server
|
||||
|
||||
## Installing
|
||||
|
||||
```bash
|
||||
go get -u src.techknowlogick.com/xormigrate
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"src.techknowlogick.com/xormigrate"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
func main() {
|
||||
db, err := xorm.NewEngine("sqlite3", "mydb.sqlite3")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
m := xormigrate.New(db, []*xormigrate.Migration{
|
||||
// create persons table
|
||||
{
|
||||
ID: "201608301400",
|
||||
// An optional description to print out to the Xormigrate logger
|
||||
Description: "Create the Person table",
|
||||
Migrate: func(tx *xorm.Engine) error {
|
||||
// it's a good pratice to copy the struct inside the function,
|
||||
// so side effects are prevented if the original struct changes during the time
|
||||
type Person struct {
|
||||
Name string
|
||||
}
|
||||
return tx.Sync2(&Person{})
|
||||
},
|
||||
Rollback: func(tx *xorm.Engine) error {
|
||||
return tx.DropTables(&Person{})
|
||||
},
|
||||
},
|
||||
// add age column to persons
|
||||
{
|
||||
ID: "201608301415",
|
||||
Migrate: func(tx *xorm.Engine) error {
|
||||
// when table already exists, it just adds fields as columns
|
||||
type Person struct {
|
||||
Age int
|
||||
}
|
||||
return tx.Sync2(&Person{})
|
||||
},
|
||||
Rollback: func(tx *xorm.Engine) error {
|
||||
// Note: Column dropping in sqlite is not support, and you will need to do this manually
|
||||
_, err = tx.Exec("ALTER TABLE person DROP COLUMN age")
|
||||
if err != nil {
|
||||
return fmt.Errorf("Drop column failed: %v", err)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
// add pets table
|
||||
{
|
||||
ID: "201608301430",
|
||||
Migrate: func(tx *xorm.Engine) error {
|
||||
type Pet struct {
|
||||
Name string
|
||||
PersonID int
|
||||
}
|
||||
return tx.Sync2(&Pet{})
|
||||
},
|
||||
Rollback: func(tx *xorm.Engine) error {
|
||||
return tx.DropTables(&Pet{})
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if err = m.Migrate(); err != nil {
|
||||
log.Fatalf("Could not migrate: %v", err)
|
||||
}
|
||||
log.Printf("Migration did run successfully")
|
||||
}
|
||||
```
|
||||
|
||||
## Having a separated function for initializing the schema
|
||||
|
||||
If you have a lot of migrations, it can be a pain to run all them, as example,
|
||||
when you are deploying a new instance of the app, in a clean database.
|
||||
To prevent this, you can set a function that will run if no migration was run
|
||||
before (in a new clean database). Remember to create everything here, all tables,
|
||||
foreign keys and what more you need in your app.
|
||||
|
||||
```go
|
||||
type Person struct {
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
|
||||
type Pet struct {
|
||||
Name string
|
||||
PersonID int
|
||||
}
|
||||
|
||||
m := xormigrate.New(db, []*xormigrate.Migration{
|
||||
// your migrations here
|
||||
})
|
||||
|
||||
m.InitSchema(func(tx *xorm.Engine) error {
|
||||
err := tx.sync2(
|
||||
&Person{},
|
||||
&Pet{},
|
||||
// all other tables of your app
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
```
|
||||
|
||||
## Adding migration descriptions to your logging
|
||||
Xormigrate's logger defaults to stdout, but it can be changed to suit your needs.
|
||||
```go
|
||||
m := xormigrate.New(db, []*xormigrate.Migration{
|
||||
// your migrations here
|
||||
})
|
||||
|
||||
// Don't log anything
|
||||
m.NilLogger()
|
||||
|
||||
// This is the default logger
|
||||
// No need to initialize this unless it was changed
|
||||
// [xormigrate] message
|
||||
m.DefaultLogger()
|
||||
|
||||
// Or, create a logger with any io.Writer you want
|
||||
m.NewLogger(os.Stdout)
|
||||
```
|
||||
|
||||
## Credits
|
||||
|
||||
* Based on [Gormigrate][gormmigrate]
|
||||
* Uses [Xorm][xorm]
|
||||
|
||||
[xorm]: http://github.com/go-xorm/xorm/
|
||||
[gormmigrate]: https://github.com/go-gormigrate/gormigrate
|
9
vendor/src.techknowlogick.com/xormigrate/go.mod
generated
vendored
Normal file
9
vendor/src.techknowlogick.com/xormigrate/go.mod
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
module src.techknowlogick.com/xormigrate
|
||||
|
||||
go 1.12
|
||||
|
||||
require (
|
||||
github.com/go-xorm/xorm v0.7.1
|
||||
github.com/joho/godotenv v1.3.0
|
||||
github.com/stretchr/testify v1.3.0
|
||||
)
|
34
vendor/src.techknowlogick.com/xormigrate/go.sum
generated
vendored
Normal file
34
vendor/src.techknowlogick.com/xormigrate/go.sum
generated
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20181014144952-4e0d7dc8888f/go.mod h1:xN/JuLBIz4bjkxNmByTiV1IbhfnYb6oo99phBn4Eqhc=
|
||||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-xorm/builder v0.3.2 h1:pSsZQRRzJNapKEAEhigw3xLmiLPeAYv5GFlpYZ8+a5I=
|
||||
github.com/go-xorm/builder v0.3.2/go.mod h1:v8mE3MFBgtL+RGFNfUnAMUqqfk/Y4W5KuwCFQIEpQLk=
|
||||
github.com/go-xorm/core v0.6.0 h1:tp6hX+ku4OD9khFZS8VGBDRY3kfVCtelPfmkgCyHxL0=
|
||||
github.com/go-xorm/core v0.6.0/go.mod h1:d8FJ9Br8OGyQl12MCclmYBuBqqxsyeedpXciV5Myih8=
|
||||
github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:56xuuqnHyryaerycW3BfssRdxQstACi0Epw/yC5E2xM=
|
||||
github.com/go-xorm/xorm v0.7.1 h1:Kj7mfuqctPdX60zuxP6EoEut0f3E6K66H6hcoxiHUMc=
|
||||
github.com/go-xorm/xorm v0.7.1/go.mod h1:EHS1htMQFptzMaIHKyzqpHGw6C9Rtug75nsq6DA9unI=
|
||||
github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ=
|
||||
github.com/jackc/pgx v3.2.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I=
|
||||
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/stretchr/testify.v1 v1.2.2/go.mod h1:QI5V/q6UbPmuhtm10CaFZxED9NreB8PnFYN9JcR6TxU=
|
92
vendor/src.techknowlogick.com/xormigrate/logger.go
generated
vendored
Normal file
92
vendor/src.techknowlogick.com/xormigrate/logger.go
generated
vendored
Normal file
@ -0,0 +1,92 @@
|
||||
package xormigrate
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
type LoggerInterface interface {
|
||||
Debug(v ...interface{})
|
||||
Debugf(format string, v ...interface{})
|
||||
Info(v ...interface{})
|
||||
Infof(format string, v ...interface{})
|
||||
Warn(v ...interface{})
|
||||
Warnf(format string, v ...interface{})
|
||||
Error(v ...interface{})
|
||||
Errorf(format string, v ...interface{})
|
||||
}
|
||||
|
||||
var (
|
||||
logger LoggerInterface = defaultLogger()
|
||||
)
|
||||
|
||||
// SetLogger sets the Xormigrate logger
|
||||
func (x *Xormigrate) SetLogger(l LoggerInterface) {
|
||||
logger = l
|
||||
}
|
||||
|
||||
func defaultLogger() *XormigrateLogger {
|
||||
return &XormigrateLogger{log.New(os.Stdout, "[xormigrate] ", 0)}
|
||||
}
|
||||
|
||||
// DefaultLogger sets a Xormigrate logger with default settings
|
||||
// e.g. "[xormigrate] message"
|
||||
func (x *Xormigrate) DefaultLogger() {
|
||||
x.SetLogger(defaultLogger())
|
||||
}
|
||||
|
||||
// NilLogger sets a Xormigrate logger that discards all messages
|
||||
func (x *Xormigrate) NilLogger() {
|
||||
x.SetLogger(&XormigrateLogger{log.New(ioutil.Discard, "", 0)})
|
||||
}
|
||||
|
||||
// NewLogger sets a Xormigrate logger with a specified io.Writer
|
||||
func (x *Xormigrate) NewLogger(writer io.Writer) {
|
||||
x.SetLogger(&XormigrateLogger{log.New(writer, "", 0)})
|
||||
}
|
||||
|
||||
type XormigrateLogger struct {
|
||||
*log.Logger
|
||||
}
|
||||
|
||||
// Debug prints a Debug message
|
||||
func (l *XormigrateLogger) Debug(v ...interface{}) {
|
||||
l.Logger.Print(v...)
|
||||
}
|
||||
|
||||
// Debugf prints a formatted Debug message
|
||||
func (l *XormigrateLogger) Debugf(format string, v ...interface{}) {
|
||||
l.Logger.Printf(format, v...)
|
||||
}
|
||||
|
||||
// Info prints an Info message
|
||||
func (l *XormigrateLogger) Info(v ...interface{}) {
|
||||
l.Logger.Print(v...)
|
||||
}
|
||||
|
||||
// Infof prints a formatted Info message
|
||||
func (l *XormigrateLogger) Infof(format string, v ...interface{}) {
|
||||
l.Logger.Printf(format, v...)
|
||||
}
|
||||
|
||||
// Warn prints a Warning message
|
||||
func (l *XormigrateLogger) Warn(v ...interface{}) {
|
||||
l.Logger.Print(v...)
|
||||
}
|
||||
|
||||
// Warnf prints a formatted Warning message
|
||||
func (l *XormigrateLogger) Warnf(format string, v ...interface{}) {
|
||||
l.Logger.Printf(format, v...)
|
||||
}
|
||||
|
||||
// Error prints an Error message
|
||||
func (l *XormigrateLogger) Error(v ...interface{}) {
|
||||
l.Logger.Print(v...)
|
||||
}
|
||||
|
||||
// Errorf prints a formatted Error message
|
||||
func (l *XormigrateLogger) Errorf(format string, v ...interface{}) {
|
||||
l.Logger.Printf(format, v...)
|
||||
}
|
5
vendor/src.techknowlogick.com/xormigrate/renovate.json
generated
vendored
Normal file
5
vendor/src.techknowlogick.com/xormigrate/renovate.json
generated
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"extends": [
|
||||
"config:base"
|
||||
]
|
||||
}
|
322
vendor/src.techknowlogick.com/xormigrate/xormigrate.go
generated
vendored
Normal file
322
vendor/src.techknowlogick.com/xormigrate/xormigrate.go
generated
vendored
Normal file
@ -0,0 +1,322 @@
|
||||
package xormigrate // import "src.techknowlogick.com/xormigrate"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
)
|
||||
|
||||
const (
|
||||
initSchemaMigrationId = "SCHEMA_INIT"
|
||||
)
|
||||
|
||||
// MigrateFunc is the func signature for migratinx.
|
||||
type MigrateFunc func(*xorm.Engine) error
|
||||
|
||||
// RollbackFunc is the func signature for rollbackinx.
|
||||
type RollbackFunc func(*xorm.Engine) error
|
||||
|
||||
// InitSchemaFunc is the func signature for initializing the schema.
|
||||
type InitSchemaFunc func(*xorm.Engine) error
|
||||
|
||||
// Migration represents a database migration (a modification to be made on the database).
|
||||
type Migration struct {
|
||||
// ID is the migration identifier. Usually a timestamp like "201601021504".
|
||||
ID string `xorm:"id"`
|
||||
// Description is the migration description, which is optionally printed out when the migration is ran.
|
||||
Description string
|
||||
// Migrate is a function that will br executed while running this migration.
|
||||
Migrate MigrateFunc `xorm:"-"`
|
||||
// Rollback will be executed on rollback. Can be nil.
|
||||
Rollback RollbackFunc `xorm:"-"`
|
||||
}
|
||||
|
||||
// Xormigrate represents a collection of all migrations of a database schema.
|
||||
type Xormigrate struct {
|
||||
db *xorm.Engine
|
||||
migrations []*Migration
|
||||
initSchema InitSchemaFunc
|
||||
}
|
||||
|
||||
// ReservedIDError is returned when a migration is using a reserved ID
|
||||
type ReservedIDError struct {
|
||||
ID string
|
||||
}
|
||||
|
||||
func (e *ReservedIDError) Error() string {
|
||||
return fmt.Sprintf(`xormigrate: Reserved migration ID: "%s"`, e.ID)
|
||||
}
|
||||
|
||||
// DuplicatedIDError is returned when more than one migration have the same ID
|
||||
type DuplicatedIDError struct {
|
||||
ID string
|
||||
}
|
||||
|
||||
func (e *DuplicatedIDError) Error() string {
|
||||
return fmt.Sprintf(`xormigrate: Duplicated migration ID: "%s"`, e.ID)
|
||||
}
|
||||
|
||||
var (
|
||||
// ErrRollbackImpossible is returned when trying to rollback a migration
|
||||
// that has no rollback function.
|
||||
ErrRollbackImpossible = errors.New("xormigrate: It's impossible to rollback this migration")
|
||||
|
||||
// ErrNoMigrationDefined is returned when no migration is defined.
|
||||
ErrNoMigrationDefined = errors.New("xormigrate: No migration defined")
|
||||
|
||||
// ErrMissingID is returned when the ID od migration is equal to ""
|
||||
ErrMissingID = errors.New("xormigrate: Missing ID in migration")
|
||||
|
||||
// ErrNoRunMigration is returned when any run migration was found while
|
||||
// running RollbackLast
|
||||
ErrNoRunMigration = errors.New("xormigrate: Could not find last run migration")
|
||||
|
||||
// ErrMigrationIDDoesNotExist is returned when migrating or rolling back to a migration ID that
|
||||
// does not exist in the list of migrations
|
||||
ErrMigrationIDDoesNotExist = errors.New("xormigrate: Tried to migrate to an ID that doesn't exist")
|
||||
)
|
||||
|
||||
// New returns a new Xormigrate.
|
||||
func New(db *xorm.Engine, migrations []*Migration) *Xormigrate {
|
||||
return &Xormigrate{
|
||||
db: db,
|
||||
migrations: migrations,
|
||||
}
|
||||
}
|
||||
|
||||
// InitSchema sets a function that is run if no migration is found.
|
||||
// The idea is preventing to run all migrations when a new clean database
|
||||
// is being migratinx. In this function you should create all tables and
|
||||
// foreign key necessary to your application.
|
||||
func (x *Xormigrate) InitSchema(initSchema InitSchemaFunc) {
|
||||
x.initSchema = initSchema
|
||||
}
|
||||
|
||||
// Migrate executes all migrations that did not run yet.
|
||||
func (x *Xormigrate) Migrate() error {
|
||||
return x.migrate("")
|
||||
}
|
||||
|
||||
// MigrateTo executes all migrations that did not run yet up to the migration that matches `migrationID`.
|
||||
func (x *Xormigrate) MigrateTo(migrationID string) error {
|
||||
if err := x.checkIDExist(migrationID); err != nil {
|
||||
return err
|
||||
}
|
||||
return x.migrate(migrationID)
|
||||
}
|
||||
|
||||
func (x *Xormigrate) migrate(migrationID string) error {
|
||||
if !x.hasMigrations() {
|
||||
return ErrNoMigrationDefined
|
||||
}
|
||||
|
||||
if err := x.checkReservedID(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := x.checkDuplicatedID(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := x.createMigrationTableIfNotExists(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if x.initSchema != nil && x.canInitializeSchema() {
|
||||
return x.runInitSchema() // return error or nil
|
||||
}
|
||||
|
||||
for _, migration := range x.migrations {
|
||||
if err := x.runMigration(migration); err != nil {
|
||||
return err
|
||||
}
|
||||
if migrationID != "" && migration.ID == migrationID {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// There are migrations to apply if either there's a defined
|
||||
// initSchema function or if the list of migrations is not empty.
|
||||
func (x *Xormigrate) hasMigrations() bool {
|
||||
return x.initSchema != nil || len(x.migrations) > 0
|
||||
}
|
||||
|
||||
// Check whether any migration is using a reserved ID.
|
||||
// For now there's only have one reserved ID, but there may be more in the future.
|
||||
func (x *Xormigrate) checkReservedID() error {
|
||||
for _, m := range x.migrations {
|
||||
if m.ID == initSchemaMigrationId {
|
||||
return &ReservedIDError{ID: m.ID}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Xormigrate) checkDuplicatedID() error {
|
||||
lookup := make(map[string]struct{}, len(x.migrations))
|
||||
for _, m := range x.migrations {
|
||||
if _, ok := lookup[m.ID]; ok {
|
||||
return &DuplicatedIDError{ID: m.ID}
|
||||
}
|
||||
lookup[m.ID] = struct{}{}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Xormigrate) checkIDExist(migrationID string) error {
|
||||
for _, migrate := range x.migrations {
|
||||
if migrate.ID == migrationID {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return ErrMigrationIDDoesNotExist
|
||||
}
|
||||
|
||||
// RollbackLast undo the last migration
|
||||
func (x *Xormigrate) RollbackLast() error {
|
||||
if len(x.migrations) == 0 {
|
||||
return ErrNoMigrationDefined
|
||||
}
|
||||
|
||||
lastRunMigration, err := x.getLastRunMigration()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return x.RollbackMigration(lastRunMigration) // return error or nil
|
||||
}
|
||||
|
||||
// RollbackTo undoes migrations up to the given migration that matches the `migrationID`.
|
||||
// Migration with the matching `migrationID` is not rolled back.
|
||||
func (x *Xormigrate) RollbackTo(migrationID string) error {
|
||||
if len(x.migrations) == 0 {
|
||||
return ErrNoMigrationDefined
|
||||
}
|
||||
|
||||
if err := x.checkIDExist(migrationID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i := len(x.migrations) - 1; i >= 0; i-- {
|
||||
migration := x.migrations[i]
|
||||
if migration.ID == migrationID {
|
||||
break
|
||||
}
|
||||
if x.migrationDidRun(migration) {
|
||||
if err := x.rollbackMigration(migration); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Xormigrate) getLastRunMigration() (*Migration, error) {
|
||||
for i := len(x.migrations) - 1; i >= 0; i-- {
|
||||
migration := x.migrations[i]
|
||||
if x.migrationDidRun(migration) {
|
||||
return migration, nil
|
||||
}
|
||||
}
|
||||
return nil, ErrNoRunMigration
|
||||
}
|
||||
|
||||
// RollbackMigration undo a migration.
|
||||
func (x *Xormigrate) RollbackMigration(m *Migration) error {
|
||||
return x.rollbackMigration(m) // return error or nil
|
||||
}
|
||||
|
||||
func (x *Xormigrate) rollbackMigration(m *Migration) error {
|
||||
if m.Rollback == nil {
|
||||
return ErrRollbackImpossible
|
||||
}
|
||||
if len(m.Description) > 0 {
|
||||
logger.Errorf("Rolling back migration: %s", m.Description)
|
||||
}
|
||||
if err := m.Rollback(x.db); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := x.db.In("id", m.ID).Delete(&Migration{}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Xormigrate) runInitSchema() error {
|
||||
logger.Info("Initializing Schema")
|
||||
if err := x.initSchema(x.db); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := x.insertMigration(initSchemaMigrationId); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, migration := range x.migrations {
|
||||
if err := x.insertMigration(migration.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Xormigrate) runMigration(migration *Migration) error {
|
||||
if len(migration.ID) == 0 {
|
||||
return ErrMissingID
|
||||
}
|
||||
|
||||
if !x.migrationDidRun(migration) {
|
||||
if len(migration.Description) > 0 {
|
||||
logger.Info(migration.Description)
|
||||
}
|
||||
if err := migration.Migrate(x.db); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := x.insertMigration(migration.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Xormigrate) createMigrationTableIfNotExists() error {
|
||||
err := x.db.Sync2(new(Migration))
|
||||
return err
|
||||
}
|
||||
|
||||
func (x *Xormigrate) migrationDidRun(m *Migration) bool {
|
||||
count, err := x.db.
|
||||
In("id", m.ID).
|
||||
Count(&Migration{})
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return count > 0
|
||||
}
|
||||
|
||||
// The schema can be initialised only if it hasn't been initialised yet
|
||||
// and no other migration has been applied already.
|
||||
func (x *Xormigrate) canInitializeSchema() bool {
|
||||
if x.migrationDidRun(&Migration{ID: initSchemaMigrationId}) {
|
||||
return false
|
||||
}
|
||||
|
||||
// If the ID doesn't exist, we also want the list of migrations to be empty
|
||||
count, err := x.db.
|
||||
Count(&Migration{})
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return count == 0
|
||||
}
|
||||
|
||||
func (x *Xormigrate) insertMigration(id string) error {
|
||||
_, err := x.db.Insert(&Migration{ID: id})
|
||||
return err
|
||||
}
|
Reference in New Issue
Block a user