migrations

package module
v2.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 27, 2023 License: MIT Imports: 14 Imported by: 0

README

hb migrations - A Better migration engine for go-pg/pg

Basic Commands

  • init
    • runs the specified intial migration as a batch on it's own.
  • migrate
    • runs all available migrations that have not been run inside a batch
  • rollback
    • reverts the last batch of migrations.
  • create name
    • creates a migration file using the name provided.

Usage

Make a main.go in a migrations folder

package main

import (
	"flag"
	"fmt"
	"log"
	"os"

	"github.com/go-pg/pg/v10"
	migrations "github.com/hbarnardt/hb_migrations"
)

const usageText = "Lorem Ipsum"

func main() {
	flag.Usage = usage
	flag.Parse()

  dbCnf := ...

	db := pg.Connect(&pg.Options{
		Addr:     dbCnf.GetHost(),
		User:     dbCnf.GetUsername(),
		Database: dbCnf.GetDatabase(),
		Password: dbCnf.GetPassword(),
	})

	migrations.SetMigrationTableName("public.migrations_home")
	migrations.SetInitialMigration("000000000000_init")
	migrations.SetMigrationNameConvention(migrations.SnakeCase)

	err := migrations.Run(db, flag.Args()...)

	if err != nil {
		log.Print(err)
		os.Exit(1)
	}
}

func usage() {
	fmt.Printf(usageText)
	flag.PrintDefaults()
	os.Exit(2)
}

Compile it:

$> go build -i -o ./migrations/migrations ./migrations/*.go

Run it:

$> ./migrations/migrations migrate

Notes on generated file names

$> ./migrations/migrations create new_index

Creates a file in the ./migrations folder called 20181031230738_new_index.go with the following contents:

package main

import (
	"github.com/go-pg/pg/v10"
	migrations "github.com/hbarnardt/hb_migrations"
)

func init() {
	migrations.Register(
		"20181031230738_new_index",
		up20181031230738NewIndex,
		down20181031230738NewIndex,
	)
}

func up20181031230738NewIndex(tx *pg.Tx) error {
	_, err := tx.Exec(``)
	return err
}

func down20181031230738NewIndex(tx *pg.Tx) error {
	_, err := tx.Exec(``)
	return err
}

Forward migration sql commands go in up and Rollback migrations sql commands go in down

Documentation

Index

Constants

View Source
const (
	// DefaultMigrationTableName is the table in which migrations will be
	// noted if not overridden in the Migrator.
	DefaultMigrationTableName = "public.hb_migrations"

	// DefaultInitialMigrationName is the name of the migration which will
	// be run by Init, if not overridden in the Migrator.
	DefaultInitialMigrationName = "000000000000_init"

	// DefaultMigrationNameConvention is the convention with which the names
	// for migration files and functions will be generated, if not overridden
	// in the Migrator.
	DefaultMigrationNameConvention = SnakeCase

	// DefaultMigrationTemplate is the template which will be used for Create,
	// when using Create without a template.
	//
	// Expects a file similar to the following to exist in the same package:
	// 	package main
	//
	// 	import (
	// 		migrations "github.com/getkalido/hb_migrations/v2"
	// 	)
	//
	// 	var (
	// 		registry	migrations.Registry
	// 	)
	//
	// 	func main() {
	// 		dbFactory := GetDB	// GetDB should return a *pg.DB.
	// 		migrator, err := migrations.NewMigrator(dbFactory, migrations.WithMigrations(&registry))
	// 		// Do things.
	// 	}
	DefaultMigrationTemplate = `package main

import (
	"github.com/go-pg/pg/v10"
	migrations "github.com/getkalido/hb_migrations/v2"
)

func init() {
	err := registry.Register(
		"{{.Filename}}",
		up{{.FuncName}},
		down{{.FuncName}},
	)
	if err != nil {
		panic(err)
	}
}

func up{{.FuncName}}(tx *pg.Tx, cont *migrations.Context) error {
	var err error
	_, err = tx.Exec(` + "`" + "`" + `)
	if err != nil {
		return err
	}
	return nil
}

func down{{.FuncName}}(tx *pg.Tx, cont *migrations.Context) error {
	var err error
	_, err = tx.Exec(` + "`" + "`" + `)
	if err != nil {
		return err
	}
	return nil
}
`
)

Variables

View Source
var (
	// ErrInvalidVerbosity indicates that verbosity has already been
	// specified when attempting to specify quiet, or that quiet has
	// already been specified when attempting to specify verbosity.
	ErrInvalidVerbosity = errors.New("verbosity already set in opposite direction")

	// ErrMigrationNotKnown indicates that a migration has been found
	// in the DB, but with no corresponding known migration.
	ErrMigrationNotKnown = errors.New("no migration by name")

	// ErrInitialMigrationNotKnown indicates that no migration was
	// found with the name of the initial migration.
	ErrInitialMigrationNotKnown = errors.New("initial migration not known")

	// ErrNoMigrationName indicates that an attempt was made to
	// create a migration, without specifying a name.
	ErrNoMigrationName = errors.New("no migration name specified")

	// ErrFileAlreadyExists indicates that an attempt was made to
	// create a migration, without specifying a name.
	ErrFileAlreadyExists = errors.New("migration file already exists")

	// ErrInvalidMigrationFuncRun indicates that a migration is being
	// run with a function with invalid function signature.
	ErrInvalidMigrationFuncRun = errors.New("invalid migration function run")
)
View Source
var (
	// ErrMigrationAlreadyExists indicates that a migration is being
	// registered with a name which has already been used.
	ErrMigrationAlreadyExists = errors.New("migration already exists")

	// ErrNullMigrationFunc indicates that a migration is being
	// registered with a null function for the up or down migration.
	ErrNullMigrationFunc = errors.New("null migration functions not allowed")

	// ErrInvalidMigrationFuncRegistered indicates that a migration is being
	// registered with a function with invalid function signature.
	ErrInvalidMigrationFuncRegistered = errors.New("invalid migration function registered")
)
View Source
var (
	// ErrUnknownNamingConvention indicates that an attempt was made to
	// create a migration, without specifying a name.
	ErrUnknownNamingConvention = errors.New("unknown naming convention")
)

Functions

func ConvertCamelCaseToSnakeCase

func ConvertCamelCaseToSnakeCase(word string) (result string)

ConvertCamelCaseToSnakeCase converts a potentially camel-case string to snake-case. Should be Unicode-safe.

Spaces are converted to underscores and any uppercase letters are replaced with an underscore and the lowercase version of the same letter.

func ConvertSnakeCaseToCamelCase

func ConvertSnakeCaseToCamelCase(word string) (result string)

ConvertSnakeCaseToCamelCase converts a potentially snake-case string to camel-case. Should be Unicode-safe.

Spaces and underscores are removed and any letter following immediately after these removed characters will be converted to uppercase.

Types

type CamelCaser

type CamelCaser struct{}

SnakeCaser will attempt to use camelCase for filenames.

func (CamelCaser) ToFileCase

func (cc CamelCaser) ToFileCase(date time.Time, input string) string

func (CamelCaser) ToFuncCase

func (cc CamelCaser) ToFuncCase(date time.Time, input string) string

type Caser

type Caser interface {
	// ToFileCase converts a description to the casing required for
	// a filename. There should not be any spaces in the output.
	ToFileCase(time.Time, string) string

	// ToFileCase converts a description to the casing required for
	// a function name. There should not be any spaces in the output.
	ToFuncCase(time.Time, string) string
}

Caser is intended to convert a description to a particular naming convention. It should take spaces into account.

func GetCaser

func GetCaser(convention MigrationNameConvention) (Caser, error)

GetCaser returns the appropriate caser for the given naming convention.

type Context added in v2.1.0

type Context struct {
	// Flavour indicates which Postgres-like API can be expected.
	Flavour PostgresFlavour
}

Context contains some additional information which may be useful for migration functions.

type DBFactory

type DBFactory func() *pg.DB

DBFactory returns a DB instance which will house both the migration table (to track completed migrations) and the tables which will be affected by the migrations.

type MigrationNameConvention

type MigrationNameConvention string

MigrationNameConvention represents a naming convention in terms of casing and underscores for a Migrator.

const (
	// CamelCase represents a camelCase naming convention.
	CamelCase MigrationNameConvention = "camelCase"

	// SnakeCase represents a snake_case naming convention.
	SnakeCase MigrationNameConvention = "snakeCase"
)

type Migrator

type Migrator struct {
	// contains filtered or unexported fields
}

Migrator can create or manage migrations as indicated by options during construction.

Should not be considered thread-safe.

func DefaultMigrator

func DefaultMigrator() *Migrator

DefaultMigrator returns a migrator with the default options.

func NewMigrator

func NewMigrator(dbFactory DBFactory, opts ...MigratorOpt) (*Migrator, error)

NewMigrator creates a Migrator with the specified options.

DefaultMigrator is used to get a default migrator, then options are applied on top of the defaults.

func (*Migrator) Create

func (m *Migrator) Create(description string) error

Create renders the default migration template to the configured migration directory.

func (*Migrator) CreateFromTemplate

func (m *Migrator) CreateFromTemplate(description string, template string) error

CreateFromTemplate renders a migration template to the configured migration directory.

func (*Migrator) Init

func (m *Migrator) Init() error

Init runs the initial migration against the configured DB. Attempting to run this without registering the initial migration is an error.

func (*Migrator) MigrateBatch

func (m *Migrator) MigrateBatch() error

MigrateStepByStep runs any migrations against the DB which have not been run yet. All migrations are run in a single migration and marked as belonging to the same batch.

func (*Migrator) MigrateStepByStep

func (m *Migrator) MigrateStepByStep() error

MigrateStepByStep runs any migrations against the DB which have not been run yet. Each migration is run in its own transaction and marked as belonging to a separate batch.

func (*Migrator) Register

func (m *Migrator) Register(
	name string,
	up interface{},
	down interface{},
) error

Register adds a migration to the list of known migrations.

If a migration by the given name is already known, this will return ErrMigrationAlreadyExists.

Valid function signatures for migration functions are:

func(*pg.Tx) error
func(*pg.Tx, *Context) error

func (*Migrator) Rollback

func (m *Migrator) Rollback() error

Rollback rolls back all migrations in the most recent batch. If the most recent group of migrations was run with MigrateStepByStep, this will only roll back the most recent migration.

type MigratorOpt

type MigratorOpt func(*Migrator) error

MigratorOpt represents an option which can be applied to a migrator during creation. See With*() functions in this package.

func WithCapacity

func WithCapacity(capacity uint) MigratorOpt

WithCapacity initialises a Migrator with enough capacity for a given number of migrations. Not necessary, but can limit allocations when building the list of migrations.

Intended for use with NewMigrator.

func WithContext

func WithContext(ctx context.Context) MigratorOpt

WithContext initialises a Migrator with a context object. This is intended to allow the migrations to be easily stopped in a CLI tool.

Intended for use with NewMigrator.

func WithExplicitLock

func WithExplicitLock() MigratorOpt

WithoutExplicitLock initialises a Migrator which will try to explicitly lock the migrations table for each transaction. Currently the default behaviour.

Intended for use with NewMigrator.

func WithInitialName

func WithInitialName(name string) MigratorOpt

WithInitialName sets the name of the initial migration which will be run by a Migrator when running the init command.

Intended for use with NewMigrator.

func WithLogger

func WithLogger(logger *log.Logger) MigratorOpt

WithLogger initialises a Migrator with a logger to use when logging output. If no logger is specified, the standard logger from the log package is used.

Intended for use with NewMigrator.

func WithMigrationDir

func WithMigrationDir(path string) MigratorOpt

WithMigrationDir initialises a Migrator with a given migration directory. When generating new migrations, they will be created in this directory.

Intended for use with NewMigrator.

func WithMigrationTableName

func WithMigrationTableName(name string) MigratorOpt

WithMigrationTableName sets the name of the table which will store completed migrations for a Migrator.

Intended for use with NewMigrator.

func WithMigrations

func WithMigrations(registry *Registry) MigratorOpt

WithMigrations loads migrations from an existing registry. Pre-emptively ensures that the Migrator has capacity for the migrations being copied.

Intended for use with NewMigrator.

func WithNameConvention

func WithNameConvention(convention MigrationNameConvention) MigratorOpt

WithNameConvention sets the name naming convention which will be used by a Migrator when generating new migrations.

Intended for use with NewMigrator.

func WithPostgresFlavour added in v2.1.0

func WithPostgresFlavour(flavour PostgresFlavour) MigratorOpt

WithPostgresFlavour initialises a Migrator with a given Postgres flavour. This is not directly used by Migrator and is merely a helper to allow migrations to act differently depending on which DB they are connected to.

Intended for use with NewMigrator.

func WithQuiet

func WithQuiet(quiet uint) MigratorOpt

WithQuiet initialises a Migrator with quiet level (default: 0). Non-zero values will decrease the amount of logging.

It is an error to set both verbosity and quiet to a non-zero value.

Intended for use with NewMigrator.

func WithTemplateDir

func WithTemplateDir(path string) MigratorOpt

WithTemplateDir initialises a Migrator with a given template directory. When searching for named templates, this directory will be used.

Intended for use with NewMigrator.

func WithVerbosity

func WithVerbosity(verbosity uint) MigratorOpt

WithVerbosity initialises a Migrator with verbosity level (default: 0). Non-zero values will increase the amount of logging.

It is an error to set both verbosity and quiet to a non-zero value.

Intended for use with NewMigrator.

func WithoutExplicitLock

func WithoutExplicitLock() MigratorOpt

WithoutExplicitLock initialises a Migrator which will not try to explicitly lock the migrations table for each transaction.

Intended for use with NewMigrator.

type PostgresFlavour added in v2.1.0

type PostgresFlavour byte

PostgresFlavour indicates the type of Postgres-like API is being connected to.

const (
	// Postgres indicates that the DB is an original Postgres instance
	// or a DB which exactly matches the Postgres API.
	Postgres PostgresFlavour = iota

	// CockroachDB indicates that the DB is a CockroachDB instance.
	// Not all Postgres functionality is supported in CockroachDB
	// and CockroachDB has several extensions to Postgres syntax.
	CockroachDB
)

type Registry

type Registry struct {
	// contains filtered or unexported fields
}

Registry holds a set of known migrations. Migrations can be registered individually with Register, or in bulk by using From to copy from another registry.

Registered migrations may be retrieved all at once with List, or individually with Get.

When it is necessary to register individual migrations in init functions, From makes it easy to copy these migrations to a registry in a Migrator.

func (*Registry) Count

func (r *Registry) Count() int

Count returns the number of migrations in the registry.

func (*Registry) EnsureCapacity

func (r *Registry) EnsureCapacity(capacity int)

EnsureCapacity increases the underlying storage of the registry, to reduce the chance of allocations when a known number of items is being added to the registry.

func (*Registry) From

func (r *Registry) From(other *Registry)

From copies registered migrations from another registry. Migrations already in the registry are thrown away.

This is a shallow copy. It is fine to add or remove items in other, as long as the items themselves are not modified after the copy.

func (*Registry) Get

func (r *Registry) Get(name string) (migration, bool)

Get returns a migration with the given name and a bool to indicate whether it has been registered.

If no migration has been registered with the given name, false will be returned.

func (*Registry) List

func (r *Registry) List() []string

List returns a slice of all registered migrations.

This is a shallow copy. It is fine to add or remove items in the registry, as long as the items themselves are not modified after the copy.

func (*Registry) Register

func (r *Registry) Register(
	name string,
	up interface{},
	down interface{},
) error

Register adds a migration to the list of known migrations.

If a migration by the given name is already known, this will return ErrMigrationAlreadyExists.

Valid function signatures for migration functions are:

func(*pg.Tx) error
func(*pg.Tx, *Context) error

func (*Registry) Sort

func (r *Registry) Sort()

Sort sorts migrations in the registry by name, lexicographically.

type SnakeCaser

type SnakeCaser struct{}

SnakeCaser will attempt to use snake_case for filenames.

func (SnakeCaser) ToFileCase

func (cc SnakeCaser) ToFileCase(date time.Time, input string) string

func (SnakeCaser) ToFuncCase

func (cc SnakeCaser) ToFuncCase(date time.Time, input string) string

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL