Migrations

You can write migrations to change database schema or data. A migration can be a regular Go function or a text file with SQL commands.

Migration names

You should put each migration into a separate file. A migration file names consists of an unique migration name (20210505110026) and a comment (add_foo_column), for example, 20210505110026_add_foo_column.go.

Migration status

go-clickhouse stores the completed migration names in the ch_migrations table to decide which migrations to run. It also uses that information to rollback migrations.

When a migration fails, go-clickhouse still marks the migration as applied so you can rollback the partially applied migration to cleanup the database and try to run the migration again.

Migration groups and rollbacks

When there are multiple migrations to run, go-clickhouse runs migrations together as a group. During rollbacks, go-clickhouse reverts the last migration group (not a single migration). Usually that is desirable, because it rolls the db back to the last known stable state.

To rollback a single migration, you need to rollback the last group, delete the migration(s) you want to skip, and run migrations again. Alternatively, you can add a new migration with the changes you need.

Go-based migrations

A Go-based migration is a regular Go function that can execute arbitrary code. Each such function must be registered in a migration collection that is created in main.go file:

package migrations

import (
	"github.com/uptrace/go-clickhouse/chmigrate"
)

// A collection of migrations.
var Migrations = chmigrate.NewMigrations()

Then, in a separate files, you should define and register migrations using MustRegister method, for example, in 20210505110026_test_migration.go:

package migrations

import (
	"context"
	"fmt"

	"github.com/uptrace/go-clickhouse/ch"
)

func init() {
	Migrations.MustRegister(func(ctx context.Context, db *ch.DB) error {
		fmt.Print(" [up migration] ")
		return nil
	}, func(ctx context.Context, db *ch.DB) error {
		fmt.Print(" [down migration] ")
		return nil
	})
}

See exampleopen in new window for details.

SQL-based migrations

A SQL-based migration is a file with .up.sql extension that contains one or more SQL commands. You can use --migration:split line as a separator to create migrations with multiple statements.

SELECT 1

--migration:split

SELECT 2

You can register such migrations using Discover method:

//go:embed *.sql
var sqlMigrations embed.FS

func init() {
	if err := Migrations.Discover(sqlMigrations); err != nil {
		panic(err)
	}
}

To create a transactional migration, use .tx.up.sql extension.

See exampleopen in new window for details.