gormigrate

package module
Version: v2.1.0 Latest Latest
Warning

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

Go to latest
Published: May 31, 2023 License: MIT Imports: 4 Imported by: 142

README

Gormigrate

Latest Release Go Reference Go Report Card

Gormigrate is a minimalistic migration helper for Gorm. Gorm already has useful migrate functions, just misses proper schema versioning and migration rollback support.

IMPORTANT: If you need support to Gorm v1 (which uses github.com/jinzhu/gorm as its import path), please import Gormigrate by using the gopkg.in/gormigrate.v1 import path.

The current Gorm version (v2) is supported by using the github.com/go-gormigrate/gormigrate/v2 import path as described in the documentation below.

Supported databases

It supports any of the databases Gorm supports:

  • MySQL
  • PostgreSQL
  • SQLite
  • SQL Server
  • TiDB
  • Clickhouse

Usage

package main

import (
	"log"

	"github.com/go-gormigrate/gormigrate/v2"
	"github.com/google/uuid"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
)

func main() {
	db, err := gorm.Open(sqlite.Open("./data.db"), &gorm.Config{
		Logger: logger.Default.LogMode(logger.Info),
	})
	if err != nil {
		log.Fatal(err)
	}

	m := gormigrate.New(db, gormigrate.DefaultOptions, []*gormigrate.Migration{{
		// create `users` table
		ID: "201608301400",
		Migrate: func(tx *gorm.DB) 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 user struct {
				ID   uuid.UUID `gorm:"type:uuid;primaryKey;uniqueIndex"`
				Name string
			}
			return tx.AutoMigrate(&user{})
		},
		Rollback: func(tx *gorm.DB) error {
			return tx.Migrator().DropTable("users")
		},
	}, {
		// add `age` column to `users` table
		ID: "201608301415",
		Migrate: func(tx *gorm.DB) error {
			// when table already exists, define only columns that are about to change
			type user struct {
				Age int
			}
			return tx.Migrator().AddColumn(&user{}, "Age")
		},
		Rollback: func(tx *gorm.DB) error {
			type user struct {
				Age int
			}
			return db.Migrator().DropColumn(&user{}, "Age")
		},
	}, {
		// create `organizations` table where users belong to
		ID: "201608301430",
		Migrate: func(tx *gorm.DB) error {
			type organization struct {
				ID      uuid.UUID `gorm:"type:uuid;primaryKey;uniqueIndex"`
				Name    string
				Address string
			}
			if err := tx.AutoMigrate(&organization{}); err != nil {
				return err
			}
			type user struct {
				OrganizationID uuid.UUID `gorm:"type:uuid"`
			}
			return tx.Migrator().AddColumn(&user{}, "OrganizationID")
		},
		Rollback: func(tx *gorm.DB) error {
			type user struct {
				OrganizationID uuid.UUID `gorm:"type:uuid"`
			}
			if err := db.Migrator().DropColumn(&user{}, "OrganizationID"); err != nil {
				return err
			}
			return tx.Migrator().DropTable("organizations")
		},
	}})

	if err = m.Migrate(); err != nil {
		log.Fatalf("Migration failed: %v", err)
	}
	log.Println("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.

type Organization struct {
	gorm.Model
	Name    string
	Address string
}

type User struct {
	gorm.Model
	Name string
	Age int
	OrganizationID uint
}

m := gormigrate.New(db, gormigrate.DefaultOptions, []*gormigrate.Migration{
    // your migrations here
})

m.InitSchema(func(tx *gorm.DB) error {
	err := tx.AutoMigrate(
		&Organization{},
		&User{},
		// all other tables of you app
	)
	if err != nil {
		return err
	}

	if err := tx.Exec("ALTER TABLE users ADD CONSTRAINT fk_users_organizations FOREIGN KEY (organization_id) REFERENCES organizations (id)").Error; err != nil {
		return err
	}
	// all other constraints, indexes, etc...
	return nil
})

Options

This is the options struct, in case you don't want the defaults:

type Options struct {
	// TableName is the migration table.
	TableName string
	// IDColumnName is the name of column where the migration id will be stored.
	IDColumnName string
	// IDColumnSize is the length of the migration id column
	IDColumnSize int
	// UseTransaction makes Gormigrate execute migrations inside a single transaction.
	// Keep in mind that not all databases support DDL commands inside transactions.
	UseTransaction bool
	// ValidateUnknownMigrations will cause migrate to fail if there's unknown migration
	// IDs in the database
	ValidateUnknownMigrations bool
}

Who is Gormigrate for?

Gormigrate was born to be a simple and minimalistic migration tool for small projects that uses Gorm. You may want to take a look at more advanced solutions like golang-migrate/migrate if you plan to scale.

Be aware that Gormigrate has no builtin lock mechanism, so if you're running it automatically and have a distributed setup (i.e. more than one executable running at the same time), you might want to use a distributed lock/mutex mechanism to prevent race conditions while running migrations.

Contributing

To run tests, first copy .sample.env as .env and edit the connection string of the database you want to run tests against. Then, run tests like below:

# running tests for PostgreSQL
go test -tags postgresql

# running test for MySQL
go test -tags mysql

# running tests for SQLite
go test -tags sqlite

# running tests for SQL Server
go test -tags sqlserver

# running test for multiple databases at once
go test -tags 'sqlite postgresql mysql'

Or alternatively, you could use Docker to easily run tests on all databases at once. To do that, make sure Docker is installed and running in your machine and then run:

task docker

Documentation

Overview

Package gormigrate is a minimalistic migration helper for Gorm (http://gorm.io)

Index

Constants

This section is empty.

Variables

View Source
var (
	// DefaultOptions can be used if you don't want to think about options.
	DefaultOptions = &Options{
		TableName:                 "migrations",
		IDColumnName:              "id",
		IDColumnSize:              255,
		UseTransaction:            false,
		ValidateUnknownMigrations: false,
	}

	// ErrRollbackImpossible is returned when trying to rollback a migration
	// that has no rollback function.
	ErrRollbackImpossible = errors.New("gormigrate: It's impossible to rollback this migration")

	// ErrNoMigrationDefined is returned when no migration is defined.
	ErrNoMigrationDefined = errors.New("gormigrate: No migration defined")

	// ErrMissingID is returned when the ID od migration is equal to ""
	ErrMissingID = errors.New("gormigrate: Missing ID in migration")

	// ErrNoRunMigration is returned when any run migration was found while
	// running RollbackLast
	ErrNoRunMigration = errors.New("gormigrate: 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("gormigrate: Tried to migrate to an ID that doesn't exist")

	// ErrUnknownPastMigration is returned if a migration exists in the DB that doesn't exist in the code
	ErrUnknownPastMigration = errors.New("gormigrate: Found migration in DB that does not exist in code")
)

Functions

This section is empty.

Types

type DuplicatedIDError

type DuplicatedIDError struct {
	ID string
}

DuplicatedIDError is returned when more than one migration have the same ID

func (*DuplicatedIDError) Error

func (e *DuplicatedIDError) Error() string

type Gormigrate

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

Gormigrate represents a collection of all migrations of a database schema.

func New

func New(db *gorm.DB, options *Options, migrations []*Migration) *Gormigrate

New returns a new Gormigrate.

func (*Gormigrate) InitSchema

func (g *Gormigrate) InitSchema(initSchema InitSchemaFunc)

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 migrating. In this function you should create all tables and foreign key necessary to your application.

func (*Gormigrate) Migrate

func (g *Gormigrate) Migrate() error

Migrate executes all migrations that did not run yet.

func (*Gormigrate) MigrateTo

func (g *Gormigrate) MigrateTo(migrationID string) error

MigrateTo executes all migrations that did not run yet up to the migration that matches `migrationID`.

func (*Gormigrate) RollbackLast

func (g *Gormigrate) RollbackLast() error

RollbackLast undo the last migration

func (*Gormigrate) RollbackMigration

func (g *Gormigrate) RollbackMigration(m *Migration) error

RollbackMigration undo a migration.

func (*Gormigrate) RollbackTo

func (g *Gormigrate) RollbackTo(migrationID string) error

RollbackTo undoes migrations up to the given migration that matches the `migrationID`. Migration with the matching `migrationID` is not rolled back.

type InitSchemaFunc

type InitSchemaFunc func(*gorm.DB) error

InitSchemaFunc is the func signature for initializing the schema.

type MigrateFunc

type MigrateFunc func(*gorm.DB) error

MigrateFunc is the func signature for migrating.

type Migration

type Migration struct {
	// ID is the migration identifier. Usually a timestamp like "201601021504".
	ID string
	// Migrate is a function that will br executed while running this migration.
	Migrate MigrateFunc
	// Rollback will be executed on rollback. Can be nil.
	Rollback RollbackFunc
}

Migration represents a database migration (a modification to be made on the database).

type Options

type Options struct {
	// TableName is the migration table.
	TableName string
	// IDColumnName is the name of column where the migration id will be stored.
	IDColumnName string
	// IDColumnSize is the length of the migration id column
	IDColumnSize int
	// UseTransaction makes Gormigrate execute migrations inside a single transaction.
	// Keep in mind that not all databases support DDL commands inside transactions.
	UseTransaction bool
	// ValidateUnknownMigrations will cause migrate to fail if there's unknown migration
	// IDs in the database
	ValidateUnknownMigrations bool
}

Options define options for all migrations.

type ReservedIDError

type ReservedIDError struct {
	ID string
}

ReservedIDError is returned when a migration is using a reserved ID

func (*ReservedIDError) Error

func (e *ReservedIDError) Error() string

type RollbackFunc

type RollbackFunc func(*gorm.DB) error

RollbackFunc is the func signature for rollbacking.

Source Files

Jump to

Keyboard shortcuts

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