Simple and pragmatic migrations for Go applications.
- Super simple driver interface to allow easy implementation for more database/migration drivers.
- Embeddable migration files.
- Support for up/down migrations.
- Atomic migrations (where possible, depending on database support).
- Support for using Go code as migrations
- Apache Phoenix
- Golang (runs generic go functions)
- MySQL
- PostgreSQL
- SQLite
// Create migration source
assetMigration := &migration.AssetMigrationSource{
Asset: Asset,
AssetDir: AssetDir,
Dir: "test-migrations",
}
// Create driver
driver, err := phoenix.New("http://localhost:8765")
// Run all up migrations
applied, err := Migrate(driver, assetMigration, migration.Up, 0)
// Remove the last 2 migrations
applied, err := Migrate(driver, assetMigration, migration.Down, 2)
Migrations are extremely simple to write:
- Separate your up and down migrations into different files. For example,
1_init.up.sql
and1_init.down.sql
. - Prefix your migration with a number or timestamp for versioning:
1_init.up.sql
or1475813115_init.up.sql
. - The file-extension can be anything you want, but must be present. For example,
1_init.up.sql
is valid, but1_init.up
is not,
Let's say we want to write our first migration to initialize the database.
In that case, we would have a file called 1_init.up.sql
containing SQL statements for the
up migration:
CREATE TABLE test_data (
id BIGINT NOT NULL PRIMARY KEY,
)
We also create a 1_init.down.sql
file containing SQL statements for the down migration:
DROP TABLE IF EXISTS test_data
We use go-bindata to embed migration files. In the
simpliest case, assuming your migration files are in migrations/
, just run:
go-bindata -o bindata.go -pkg myapp migrations/
Then, use AssetMigrationSource
to find the migrations:
assetMigration := &migration.AssetMigrationSource{
Asset: Asset,
AssetDir: AssetDir,
Dir: "test-migrations",
}
The Asset
and AssetDir
functions are generated by go-bindata
.
Sometimes, we might be working with a database or have a situation where the query language is not expressive enough to perform the required migrations. For example, we might have to get some data out of the database, perform some transformations and then write it back. For these type of situations, you can use Go for migrations.
When using Go for migrations, create a golang.Source
using golang.NewSource()
. Then, simply add migrations to the source
using the AddMigration()
method. You will need to pass in the name of the migration without the extension and direction, e.g.
1_init
. For the second parameter, pass in the direction (migration.Up
or migration.Down
) and for the third parameter,
pass in a function or method with this signature: func(c *golangConfig) error
for running the migration.
If your migrations need to access configuration values or database clients, create a golang.Config
struct using
golang.NewConfig()
. This is concurrency-safe, and you can set values into it using Set()
and retrieve values using Get()
.
Finally, you need to define 2 functions:
- A function for writing or deleting an applied migration matching this signature:
func(config *golang.Config, id string, direction migration.Direction) error
- A function for getting a list of applied migrations matching this signature:
func(config *golang.Config) ([]string, error)
These are required for initializing the driver:
driver, err := golang.New(source, updateVersion, applied, config)
Here's a quick example:
source := golang.NewSource()
source.AddMigration("1_init", migration.Up, func(c *golang.Config) error {
// Run up migration here
// If required, you can retrieve configuration here: something := c.Get("something")
})
source.AddMigration("1_init", migration.Down, func(c *golang.Config) error {
// Run down migration here
})
// Create config
config := golang.NewConfig()
config.Set("test", "test")
// Define functions
applied := func(c *golang.Config) ([]string, error) {
// Return list of applied migrations
}
updateVersion := func(id string, direction migration.Direction, c *golangC.onfig) error {
// Write or delete applied migration in storage
}
// Create driver
driver, err := golang.New(source, updateVersion, applied, config)
// Run migrations
count, err = migration.Migrate(driver, source, migration.Up, 0)
- Command line program to run migrations
- MigrationSource that uses migrations from the local file system
- More drivers
We wanted a migration library with the following features:
- Open to extension for all sorts of databases, not just
database/sql
drivers or an ORM. - Easily embeddable in a Go application.
- Support for embedding migration files directly into the app.
We narrowed our focus down to 2 contenders: sql-migrate and migrate
sql-migrate
leans heavily on the gorp ORM library to perform migrations.
Unfortunately, this means that we were restricted to databases supported by gorp
. It is easily embeddable in a
Go app and supports embedding migration files directly into the Go binary. If database support was a bit more flexible,
we would have gone with it.
migrate
is highly extensible, and adding support for another database is extremely trivial. However, due to it using
the scheme in the dsn to determine which database driver to use, it prevented us from easily implementing an Apache
Phoenix driver, which uses the scheme to determine if we should connect over http
or https
. Due to the way the
project is structured, it was also almost impossible to add support for embeddable migration files without major
changes.
This library is licensed under the Apache 2 License.