Dead simple Go database migration library.

DroneCI Go Report Card codecov DroneCI GoDoc License: Apache 2.0

migrator

Dead simple Go database migration library.

Features

  • Simple code
  • Usage as a library, embeddable and extensible on your behalf
  • Support of any database supported by database/sql
  • Go code migrations, either transactional or transaction-less, using *sql.Tx (migrator.Migration) or *sql.DB (migrator.MigrationNoTx)
  • No need to use packr, gobin or others, since all migrations are just Go code

Compatibility

Although any database supported by database/sql and one of its recommended drivers SQLDrivers should work OK, at the moment only PostgreSQL and MySQL are being explicitly tested.

If you find any issues with any of the databases included under the umbrella of database/sql, feel free to contribute by opening an issue or sending a pull request.

Usage

The following example assumes:

  • A working postgres DB conn on localhost, with a user named postgres, empty password, and db named foo

Customize this to your needs by changing the driver and/or connection settings.

QuickStart:

package main

import (
	"database/sql"
	"log"

	_ "github.com/jackc/pgx/v4/stdlib" // postgres driver
	"github.com/lopezator/migrator"
)

func main() {
    // Configure migrations
    m, err := migrator.New(
        migrator.Migrations(
            &migrator.Migration{
                Name: "Create table foo",
                Func: func(tx *sql.Tx) error {
                    if _, err := tx.Exec("CREATE TABLE foo (id INT PRIMARY KEY)"); err != nil {
                        return err
                    }
                    return nil
                },
            },
        ),
    )
    if err != nil {
        log.Fatal(err)
    }
   
    // Open database connection
    db, err := sql.Open("pgx", "postgres://postgres@localhost/foo?sslmode=disable")
    if err != nil {
        log.Fatal(err)
    }
    
    // Migrate up
    if err := m.Migrate(db); err != nil {
        log.Fatal(err)
    }
}

Notes on examples above:

  • Migrator creates/manages a table named migrations to keep track of the applied versions. However, if you want to customize the table name migrator.TableName("my_migrations") can be passed to migrator.New function as an additional option.

Logging

By default, migrator prints applying/applied migration info to stdout. If that's enough for you, you can skip this section.

If you need some special formatting or want to use a 3rd party logging library, this could be done by using WithLogger option as follows:

logger := migrator.WithLogger(migrator.LoggerFunc(func(msg string, args ...interface{}) {
	// Your code here 
})))

Then you will only need to pass the logger as an option to migrator.New.

Looking for more examples?

Just examine the migrator_test.go file.

But I don't want to write complex migrations in strings! 😥

You still can use your favorite embedding tool to write your migrations inside .sql files and load them into migrator!

I provide a simple example using esc on the Using tx, one embedded query test here: migrator_test

Erm... Where are the ID's of the migrations to know their order? 🤔

In order to avoid problems with different identifiers, ID collisions, etc... the order of the migrations is just the order being passed to the migrator.

Wait... no down migrations? 😱

Adding the functionality to reverse a migration introduces complexity to the API, the code, and the risk of losing the synchrony between the defined list of migrations and current state of the database. In addition to this, depending on the case, not all the migrations are easily reversible, or they cannot be reversed.

We also think that it's a good idea to follow an "append-only" philosophy when coming to database migrations, so correcting a defective migration comes in the form of adding a new migration instead of reversing it.

e.g. After a CREATE TABLE foo we'll simply add a new DROP TABLE foo instead of reverting the first migration, so both states are reflected both in the code and the database.

Caveats

  • The name of the migrations must be SQL-safe for your engine of choice. Avoiding conflicting characters like ' is recommended, otherwise, you will have to escape them by yourself e.g. '' for PostgreSQL and \' for MySQL.

Motivation

Why another migration library?

  • Lightweight dummy implementation with just database/sql support. Migrator doesn't need any ORM or other heavy libraries as a dependency. It's just made from a single file in less than 200 lines of code!
  • Easily embedabble into your application, no need to install/use a separate binary
  • Supports Go migrations, either transactional or transaction-less
  • Flexible usage

These are not migrator objectives

  • Add support to databases outside database/sql
  • Complicate the code/logic to add functionality that could be accomplished easily on userland, like view current version, list of applied versions, etc.
  • Add a bunch of dependencies just to provide a CLI/standalone functionality

Comparison with other tools

  • rubenv/sql-migrate doesn't support Go migrations. Sometimes you need Go code to accomplish complex tasks that can't be done using just SQL.

  • Boostport/migration is a nice tool with support for many databases. Migrator code is inspired by its codebase. It supports both Go and SQL migrations. Unfortunately, when using Go migrations you have to write your own logic to retrieve and update version info in the database. Additionally I didn't find a nice way to encapsulate both migration and version logic queries inside the same transaction.

  • golang-migrate/migrate doesn't support Go migrations. Sometimes you need Go code to accomplish complex tasks that couldn't be done using just SQL. Additionally it feels a little heavy for the task.

  • pressly/goose supports both Go and SQL migrations. Unfortunately it doesn't support transaction-less Go migrations. Sometimes using transactions is either not possible with the combination of queries you need in a single migration, or others could be very slow and you simply don't need them for that specific case. It's also pretty big, with internals that are difficult to follow. It's crowded with a lot of functionality that could be done in userland pretty fast.

Contribute

Pull requests are welcome, this is an early implementation and work is needed in all areas: docs, examples, tests, ci...

The easiest way to contribute is by installing docker and docker-compose, and ensure you comply with code standards and pass all the tests before submitting a PR by running:

$> docker-compose up -d --build
$> docker-compose exec migrator make prepare
$> docker-compose exec migrator make sanity-check
$> docker-compose exec migrator make test
$> docker-compose down

Make sure you also provide relevant information in your PR as detailed in the pull request template.

Logo

The logo was taken from @ashleymcnamara's gophers repo. I've just applied slight modifications to it.

Comments
  • logger: implement logger option

    logger: implement logger option

    Add a Logger interface, along with a LoggerFunc and a new WithLogger option.

    This would enable:

    • Customizing migrator output
    • Integrating a 3rd party logging tool

    By default, migrator will work as before, just outputting to stdout.

    Closes #6

  • config or opt-out for stdout logging

    config or opt-out for stdout logging

    Hey @lopezator! Thanks for this quick library.

    I run these migrations a lot in tests and the fmt.Printf calls fill test results on errors (or from setting -v). I'd like an option to disable/opt-out of those calls (which I'll set in tests).

    What about func (m *Migrator) DisableLogging() (or SetLogger) call?

    Thoughts?

  • example in readme doesn't compile

    example in readme doesn't compile

    Hi, i'm trying to run the quick start example: it doesn't compile.

    My env:

    • go version: go version go1.13.3 linux/amd64

    How to reproduce

    • docker run -it --rm golang:1.13.3-stretch bash
    • mkdir -p /go/src/bob && cd $_
    • copy and paste the quick start example in main.go
    • run go build main.go

    expected results

    • no error

    actual results

    # bob                                               
    ./main.go:12:4: assignment mismatch: 1 variable but migrator.New returns 2 values
    ./main.go:13:3: undefined: Migrations
    
  • migrations table optional name

    migrations table optional name

    a suggestion to allow to set the migrations version table name, something that can be useful if there are several versions to manage (like in case there's a library using migrator)

  • Export the migration interface to allow dynamically building the migration list

    Export the migration interface to allow dynamically building the migration list

    I'm trying to use esc to load my migrations dynamically, but since I can't create the right type for a list I can't make it work.

    Long story short, the following code grabs all the files from esc sorts them by name (they're named 001-foo.sql, 002-bar.sql, etc) and then tries to add them to migrator.

        nMigrations := len(_escDirs["."])
        names := make([]string, nMigrations)
        sort.Strings(names)
    
        migrations := make([]*migrator.Migration, nMigrations)
        for i, name := range names {
            data, err := _escStatic.prepare(name)
            if err != nil {
                return err
            }
    
            migrations[i] = &migrator.Migration{
                Name: name,
                Func: func(tx *sql.Tx) error {
                    _, err := tx.Exec(string(data.data))
    
                    return err
                },
            }
        }
    
        migrator := migrator.New(migrations...)
    
        return migrator.Migrate(db)
    

    But due to New only taking an interface I can't create a slice of I'm SOL...

    So long story short, would you consider a merging a PR that is exposed to make this possible?

  • Make Name be SQL safe before inserting

    Make Name be SQL safe before inserting

    When trying to use a Name with SQL-reserved characters like ' it dramatically fails:

    migrator: error while running migrations: error updating migration versions: pq: syntax error at or near \"myvalue\"
    

    For example:

    &migrator.MigrationNoTx{
    	Name: "table: refactor 'myvalue/*' to 'prefixed-myvalue/*'",
    	Func: func(db *sql.DB) error {
    		[...]
    	},
    }
    

    It should be relatively easy to solve the issue by escaping the Name before performing the insert.

  • migrator: add functional options

    migrator: add functional options

    This allows customizing migrator behaviour by exposing it's configurable parts through functional options. This change starts by adding a Migrations and TableName options to fullfil current requirements.

    Closes #13 Superseeds #14

  • List of pending migrations

    List of pending migrations

    Thanks for providing this great minimalistic package.

    Would you be open to a function that returns the pending migrations based on the migrations table state? This would allow to show upfront what Migrate(db) would do.

  • SQL-agnostic version of migrator

    SQL-agnostic version of migrator

    Hi,

    I really like migrator! Overall, it seems like the simplest and versatile migration library in Go I have seen so far.

    I have a question: Is there any intend to abstract away the dependency on the sql package? I have a Node.js background and I loved working with https://github.com/sequelize/umzug because it enabled me to store the migration state in a JSON file and I could essentially write migrations that aren't even related to SQL.

    Thanks in advance and stay safe!

  • make sql statements customizable via options

    make sql statements customizable via options

    Hi! great lib!

    Not sure how you'll feel about this changes, since you didn't asked for them. But I needed this for my use case. Mainly, I needed to be able to override the CREATE stmt for the migrations table, since my DB is not exactly SQL compliant. Also, I tried to reduce the use of string interpolation for SQL.

  • migrations table, add a timestamp to rows

    migrations table, add a timestamp to rows

    Consider adding a creation timestamp to each row, created_at.

    This should be done in a nice way for current users of the tool, detecting and migrating their migrator table to add the newly added column without breaking things and requiring manual intervention.

  • Refactor tests

    Refactor tests

    migration_test.go starts to being hard to reason about and all tests are sharing the same db state on ci.

    good options to decouple db state between tests might be:

    • dockertest
    • datadog txdb

    additionally a refactor is need to make it more understandable and extensible.

  • Testing method support

    Testing method support

    What do you think about adding a method that executes just after the migration step and before increasing the current migration version? Mainly to be sure that everything worked as expected.

    Something like:

    &migrator.Migration{
    	Name: "my migration",
    	Func: func(tx *sql.Tx) error {
    		// add migration logic here.
    	},
    	TestFunc: func(tx *sql.Tx) error {
    		// add testing logic here.
            },
    }
    
Migration - Commonly used migration tools

Migration Commonly used migration tools Usage package main import ( "context"

Feb 16, 2022
Migration - Commonly used migration tools

Migration Commonly used migration tools Usage package main import ( "context"

Feb 16, 2022
Dbmate is a database migration tool, to keep your database schema in sync across multiple developers and your production servers.

Dbmate is a database migration tool, to keep your database schema in sync across multiple developers and your production servers. It is a stand

Jan 1, 2023
Goose database migration tool - fork of https://bitbucket.org/liamstask/goose

goose Goose is a database migration tool. Manage your database schema by creating incremental SQL changes or Go functions. Goals of this fork github.c

Dec 30, 2022
Minimalistic database migration helper for Gorm ORM

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

Dec 25, 2022
Database migration through structures - development

goMigration 基于 Golang 的数据库迁移工具,目前仍在开发中,有兴趣的小伙伴可以联系我一起~ 食用方法 go get https://github.com/DGuang21/goMigration 手动将其安装 可通过 gom gen create_c_user_table 方法生

Dec 2, 2021
A database migration tool written in Go.

dbmagritte created by Austin Poor A database migration tool written in Go. Usage Commands: init: Set up the repo by creating a .dbmagritte.yaml file a

Jan 29, 2022
A migration engine to deploy database changes in your golang + mongodb app.

bisonmigration A migration engine to deploy database changes in your golang + mongodb app. Migration files register their UP and DOWN functions in the

Jan 30, 2022
Simple Migration Tool - written in Go

Pravasan Simple Migration tool intend to be used for any languages, for any db. Please feel free to criticize, comment, etc. Currently this is working

Sep 26, 2022
Simple migration tool for MySQL

prrn Simple migration tool for MySQL This is a CLI that helps you create a DB migration file. There is no need to write up and down files from scratch

Nov 10, 2021
SQL schema migration tool for Go.

sql-migrate SQL Schema migration tool for Go. Based on gorp and goose. Using modl? Check out modl-migrate. Features Usable as a CLI tool or as a libra

Jan 2, 2023
A tool to compare if terraform provider migration schema snapshot is equal to schema defined in resource code

migration schema comparer for Terraform When develop Terraform provider sometimes we need do some state migration(not schema migration) via StateUpgra

Nov 18, 2021
Database schema evolution library for Go

Try browsing the code on Sourcegraph! Darwin Database schema evolution library for Go Example package main import ( "database/sql" "log" "github.

Dec 5, 2022
Django style fixtures for Golang's excellent built-in database/sql library.

go-fixtures Django style fixtures for Golang's excellent built-in database/sql library. Currently only YAML fixtures are supported. There are two rese

Sep 26, 2022
Database migrations. CLI and Golang library.

migrate Database migrations written in Go. Use as CLI or import as library. Migrate reads migrations from sources and applies them in correct order to

Dec 31, 2022
Database migrations. CLI and Golang library.

Database migrations written in Go. Use as CLI or import as library.

May 30, 2021
goydb, a couchdb compatible embeddable database written in go
goydb, a couchdb compatible embeddable database written in go

goydb, a couchdb compatible embeddable database written in go Getting started (not embedded) Using docker mkdir data docker run -e GOYDB_ADMINS=admin:

Sep 14, 2022
mini tools handling migrasion database from cli

mini tools handling migrasion database from cli

Dec 13, 2021
Opinionated tool for database structure management and migrations

trek Requirements At least version 13 of postgres is needed. Installation go install . Setup Create config.yaml: model_name: <model_name> db_name: <db

Dec 14, 2022