Migrations

The migrations plugin can automatically generate and apply migrations to keep your database schema up to date when changes to the model definitions occur.

- cmd/
    - sqlbunny/      <- Runs sqlbunny, with your own configuration
        - main.go
    - migrate/       <- Runs your migrations
        - main.go
    - app/           <- Runs your application
        - main.go
- migrations/        <- Package containing migrations.
    - store.go
    - migration_00001.go
- models/            <- autogenerated models package
    - book.go        
    - bunny_*.go

Setting up

First, make sure that the migrations plugin is enabled:

Run(
    &migration.Plugin{},
    ...
)

To create the migrations package, run:

go run ./cmd/sqlbunny/main.go migration gen

The first run will create the package, and the migration store in migrations/store.go.

Then, add the newly created migration store to the plugin config. This is necessary so the plugin can read the existing migrations, to diff the existing schema with the new one.

Run(
    &migration.Plugin{
        Store: &migrations.Store,
    },
    ...
)

Generating migrations

To generate a migration, run:

go run ./cmd/sqlbunny/main.go migration gen

Migrations are generated according to the following algorithm:

  • Existing migrations are applied to a "virtual" schema. This schema represents the current state of the database.
  • The model definitions from the config are compiled into another "virtual" schema. This schema represents the desired final state of the database.
  • The two schemas are examined for differences
  • The differences are taken into a migration that converts the current schema to the desired schema.
  • The migration is written to a Go file.

Generated migration files should be checked into version control (Git, SVN, etc.). They're effectively the history of your models, and keeping it intact ensures any developer or production database is able to be migrated up to the latest changes.

Running migrations

To run the migrations, call Store.Run(ctx) on your migrations package.

You can do this from your main application, or you can build a third separate binary. Having a separate binary may be useful for development, so you can run migrations even when your main application code is in a state that doesn't compile.

Put this in cmd/migrate/main.go:

package main

import (
	"context"
	"database/sql"

	"github.com/sqlbunny/sqlbunny/runtime/bunny"
	"github.com/sqlbunny/sqlbunny_demo/migrations"
)

func main() {
	db, err := sql.Open("postgres", "host=localhost port=5432 dbname=postgres user=postgres password=postgres sslmode=disable")
	if err != nil {
		panic(err)
	}

	ctx := context.Background()
	ctx = bunny.ContextWithDB(ctx, db)

	err = migrations.Store.Run(ctx)
	if err != nil {
		panic(err)
	}
}

The migration store tracks which migrations have been run in the migrations table. This table is created on the fly, and it's not managed by migrations itself (ie, it's not a model).

When running, it will read which migrations have been applied from the migrations table, and then apply the missing ones (if any) in order.

Manual migrations

TODO