File: README.md
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 a database. - Drivers are "dumb", migrate glues everything together and makes sure the logic is bulletproof.
(Keeps the drivers lightweight, too.) - Database drivers don't assume things or try to correct user input. When in doubt, fail.
Forked from mattes/migrate
Databases
Database drivers run migrations. Add a new database?
- PostgreSQL
- PGX v4
- PGX v5
- Redshift
- Ql
- Cassandra / ScyllaDB
- SQLite
- SQLite3 (todo #165)
- SQLCipher
- MySQL / MariaDB
- Neo4j
- MongoDB
- CrateDB (todo #170)
- Shell (todo #171)
- Google Cloud Spanner
- CockroachDB
- YugabyteDB
- ClickHouse
- Firebird
- MS SQL Server
- rqlite
Database URLs
Database connection strings are specified via URLs. The URL format is driver dependent but generally has the form: dbdriver://username:password@host:port/dbname?param1=true¶m2=false
Any reserved URL characters need to be escaped. Note, the % character also needs to be escaped
Explicitly, the following characters need to be escaped:!, #, $, %, &, ', (, ), *, +, ,, /, :, ;, =, ?, @, [, ]
It's easiest to always run the URL parts of your DB connection URL (e.g. username, password, etc) through an URL encoder. See the example Python snippets below:
$ python3 -c 'import urllib.parse; print(urllib.parse.quote(input("String to encode: "), ""))'
String to encode: FAKEpassword!#$%&'()*+,/:;=?@[]
FAKEpassword%21%23%24%25%26%27%28%29%2A%2B%2C%2F%3A%3B%3D%3F%40%5B%5D
$ python2 -c 'import urllib; print urllib.quote(raw_input("String to encode: "), "")'
String to encode: FAKEpassword!#$%&'()*+,/:;=?@[]
FAKEpassword%21%23%24%25%26%27%28%29%2A%2B%2C%2F%3A%3B%3D%3F%40%5B%5D
$Migration Sources
Source drivers read migrations from local or remote sources. Add a new source?
- Filesystem - read from filesystem
- io/fs - read from a Go io/fs
- Go-Bindata - read from embedded binary data (jteeuwen/go-bindata)
- pkger - read from embedded binary data (markbates/pkger)
- GitHub - read from remote GitHub repositories
- GitHub Enterprise - read from remote GitHub Enterprise repositories
- Bitbucket - read from remote Bitbucket repositories
- Gitlab - read from remote Gitlab repositories
- AWS S3 - read from Amazon Web Services S3
- Google Cloud Storage - read from Google Cloud Platform Storage
CLI usage
- Simple wrapper around this library.
- Handles ctrl+c (SIGINT) gracefully.
- No config search paths, no config files, no magic ENV var injections.
CLI Documentation (includes CLI install instructions)
Basic usage
$ migrate -source file://path/to/migrations -database postgres://localhost:5432/database up 2Docker usage
$ docker run -v {{ migration dir }}:/migrations --network host migrate/migrate
-path=/migrations/ -database postgres://localhost:5432/database up 2Use in your Go project
- API is stable and frozen for this release (v3 & v4).
- Uses Go modules to manage dependencies.
- To help prevent database corruptions, it supports graceful stops via
GracefulStop chan bool. - Bring your own logger.
- Uses
io.Readerstreams internally for low memory overhead. - Thread-safe and no goroutine leaks.
import (
"github.com/golang-migrate/migrate/v4"
_ "github.com/golang-migrate/migrate/v4/database/postgres"
_ "github.com/golang-migrate/migrate/v4/source/github"
)
func main() {
m, err := migrate.New(
"github://mattes:personal-access-token@mattes/migrate_test",
"postgres://localhost:5432/database?sslmode=enable")
m.Steps(2)
}Want to use an existing database client?
import (
"database/sql"
_ "github.com/lib/pq"
"github.com/golang-migrate/migrate/v4"
"github.com/golang-migrate/migrate/v4/database/postgres"
_ "github.com/golang-migrate/migrate/v4/source/file"
)
func main() {
db, err := sql.Open("postgres", "postgres://localhost:5432/database?sslmode=enable")
driver, err := postgres.WithInstance(db, &postgres.Config{})
m, err := migrate.NewWithDatabaseInstance(
"file:///migrations",
"postgres", driver)
m.Up() // or m.Steps(2) if you want to explicitly set the number of migrations to run
}Getting started
Go to getting started
Tutorials
(more tutorials to come)
Migration files
Each migration has an up and down migration. Why?
1481574547_create_users_table.up.sql
1481574547_create_users_table.down.sqlBest practices: How to write migrations.
Coming from another db migration tool?
Check out migradaptor.
Note: migradaptor is not affiliated or supported by this project
Versions
| Version | Supported? | Import | Notes |
|---|---|---|---|
| master | :white_check_mark: | import "github.com/golang-migrate/migrate/v4" |
New features and bug fixes arrive here first |
| v4 | :white_check_mark: | import "github.com/golang-migrate/migrate/v4" |
Used for stable releases |
| v3 | :x: | import "github.com/golang-migrate/migrate" (with package manager) or import "gopkg.in/golang-migrate/migrate.v3" (not recommended) |
DO NOT USE - No longer supported |
Development and Contributing
Yes, please! Makefile is your friend,
read the development guide.
Also have a look at the FAQ.
Looking for alternatives? https://awesome-go.com/#database.
File: cmd/migrate/README.md
migrate CLI
Installation
Download pre-built binary (Windows, MacOS, or Linux)
$ curl -L https://github.com/golang-migrate/migrate/releases/download/$version/migrate.$os-$arch.tar.gz | tar xvzMacOS
$ brew install golang-migrateWindows
Using scoop
$ scoop install migrateLinux (*.deb package)
$ curl -fsSL https://packagecloud.io/golang-migrate/migrate/gpgkey | sudo gpg --dearmor -o /etc/apt/keyrings/migrate.gpg
$ echo "deb [signed-by=/etc/apt/keyrings/migrate.gpg] https://packagecloud.io/golang-migrate/migrate/ubuntu/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/migrate.list
$ apt-get update
$ apt-get install -y migrateWith Go toolchain
Versioned
$ go get -u -d github.com/golang-migrate/migrate/cmd/migrate
$ cd $GOPATH/src/github.com/golang-migrate/migrate/cmd/migrate
$ git checkout $TAG # e.g. v4.1.0
$ # Go 1.15 and below
$ go build -tags 'postgres' -ldflags="-X main.Version=$(git describe --tags)" -o $GOPATH/bin/migrate $GOPATH/src/github.com/golang-migrate/migrate/cmd/migrate
$ # Go 1.16+
$ go install -tags 'postgres' github.com/golang-migrate/migrate/v4/cmd/migrate@$TAGUnversioned
$ # Go 1.15 and below
$ go get -tags 'postgres' -u github.com/golang-migrate/migrate/cmd/migrate
$ # Go 1.16+
$ go install -tags 'postgres' github.com/golang-migrate/migrate/v4/cmd/migrate@latestNotes
- Requires a version of Go that supports modules. e.g. Go 1.11+
- These examples build the cli which will only work with postgres. In order
to build the cli for use with other databases, replace thepostgresbuild tag
with the appropriate database tag(s) for the databases desired. The tags
correspond to the names of the sub-packages underneath thedatabasepackage. - Similarly to the database build tags, if you need to support other sources, use the appropriate build tag(s).
- Support for build constraints will be removed in the future: https://github.com/golang-migrate/migrate/issues/60
- For versions of Go 1.15 and lower, make sure you're not installing the
migrateCLI from a module. e.g. there should not be anygo.modfiles in your current directory or any directory from your current directory to the root
Usage
$ migrate -help
Usage: migrate OPTIONS COMMAND [arg...]
migrate [ -version | -help ]
Options:
-source Location of the migrations (driver://url)
-path Shorthand for -source=file://path
-database Run migrations against this database (driver://url)
-prefetch N Number of migrations to load in advance before executing (default 10)
-lock-timeout N Allow N seconds to acquire database lock (default 15)
-verbose Print verbose logging
-version Print version
-help Print usage
Commands:
create [-ext E] [-dir D] [-seq] [-digits N] [-format] [-tz] NAME
Create a set of timestamped up/down migrations titled NAME, in directory D with extension E.
Use -seq option to generate sequential up/down migrations with N digits.
Use -format option to specify a Go time format string. Note: migrations with the same time cause "duplicate migration version" error.
Use -tz option to specify the timezone that will be used when generating non-sequential migrations (defaults: UTC).
goto V Migrate to version V
up [N] Apply all or N up migrations
down [N] [-all] Apply all or N down migrations
Use -all to apply all down migrations
drop [-f] Drop everything inside database
Use -f to bypass confirmation
force V Set version V but don't run migration (ignores dirty state)
version Print current migration versionSo let's say you want to run the first two migrations
$ migrate -source file://path/to/migrations -database postgres://localhost:5432/database up 2If your migrations are hosted on github
$ migrate -source github://mattes:personal-access-token@mattes/migrate_test \
-database postgres://localhost:5432/database down 2The CLI will gracefully stop at a safe point when SIGINT (ctrl+c) is received.
Send SIGKILL for immediate halt.
Reading CLI arguments from somewhere else
ENV variables
$ migrate -database "$MY_MIGRATE_DATABASE"JSON files
Check out https://stedolan.github.io/jq/
$ migrate -database "$(cat config.json | jq -r '.database')"YAML files
$ migrate -database "$(cat config/database.yml | ruby -ryaml -e "print YAML.load(STDIN.read)['database']")"
$ migrate -database "$(cat config/database.yml | python -c 'import yaml,sys;print yaml.safe_load(sys.stdin)["database"]')"File: database/cockroachdb/README.md
cockroachdb
cockroachdb://user:password@host:port/dbname?query (cockroach://, and crdb-postgres:// work, too)
| URL Query | WithInstance Config | Description |
|---|---|---|
x-migrations-table |
MigrationsTable |
Name of the migrations table |
x-lock-table |
LockTable |
Name of the table which maintains the migration lock |
x-force-lock |
ForceLock |
Force lock acquisition to fix faulty migrations which may not have released the schema lock (Boolean, default is false) |
dbname |
DatabaseName |
The name of the database to connect to |
user |
The user to sign in as | |
password |
The user's password | |
host |
The host to connect to. Values that start with / are for unix domain sockets. (default is localhost) | |
port |
The port to bind to. (default is 5432) | |
connect_timeout |
Maximum wait for connection, in seconds. Zero or not specified means wait indefinitely. | |
sslcert |
Cert file location. The file must contain PEM encoded data. | |
sslkey |
Key file location. The file must contain PEM encoded data. | |
sslrootcert |
The location of the root certificate file. The file must contain PEM encoded data. | |
sslmode |
Whether or not to use SSL (disable|require|verify-ca|verify-full) |
File: database/clickhouse/README.md
ClickHouse
clickhouse://host:port?username=user&password=password&database=clicks&x-multi-statement=true
| URL Query | Description |
|---|---|
x-migrations-table |
Name of the migrations table |
x-migrations-table-engine |
Engine to use for the migrations table, defaults to TinyLog |
x-cluster-name |
Name of cluster for creating schema_migrations table cluster wide |
database |
The name of the database to connect to |
username |
The user to sign in as |
password |
The user's password |
host |
The host to connect to. |
port |
The port to bind to. |
x-multi-statement |
false |
Notes
- The Clickhouse driver does not natively support executing multiple statements in a single query. To allow for multiple statements in a single migration, you can use the
x-multi-statementparam. There are two important caveats:- This mode splits the migration text into separately-executed statements by a semi-colon
;. Thusx-multi-statementcannot be used when a statement in the migration contains a string with a semi-colon. - The queries are not executed in any sort of transaction/batch, meaning you are responsible for fixing partial migrations.
- This mode splits the migration text into separately-executed statements by a semi-colon
- Using the default TinyLog table engine for the schema_versions table prevents backing up the table if using the clickhouse-backup tool. If backing up the database with make sure the migrations are run with
x-migrations-table-engine=MergeTree. - Clickhouse cluster mode is not officially supported, since it's not tested right now, but you can try enabling
schema_migrationstable replication by specifying ax-cluster-name:- When
x-cluster-nameis specified,x-migrations-table-enginealso should be specified. See the docs regarding replicated table engines. - When
x-cluster-nameis specified, only theschema_migrationstable is replicated across the cluster. You still need to write your migrations so that the application tables are replicated within the cluster.
- When
- If you want to create database inside the migration, you should know, that table which will manage migrations
schema-migrations tablewill be indefaulttable, so you can't useUSE <database_name>inside migration. In this case you may not specify the database in the connection string (example you can find here)
File: database/mongodb/README.md
MongoDB
- Driver work with mongo through db.runCommands
- Migrations support json format. It contains array of commands for
db.runCommand. Every command is executed in separate request to database - All keys have to be in quotes
" - Examples
Usage
mongodb://user:password@host:port/dbname?query (mongodb+srv:// also works, but behaves a bit differently. See docs for more information)
| URL Query | WithInstance Config | Description |
|---|---|---|
x-migrations-collection |
MigrationsCollection |
Name of the migrations collection |
x-transaction-mode |
TransactionMode |
If set to true wrap commands in transaction. Available only for replica set. Driver is using strconv.ParseBool for parsing |
x-advisory-locking |
true |
Feature flag for advisory locking, if set to false, disable advisory locking |
x-advisory-lock-collection |
migrate_advisory_lock |
The name of the collection to use for advisory locking. |
x-advisory-lock-timeout |
15 |
The max time in seconds that migrate will wait to acquire a lock before failing. |
x-advisory-lock-timeout-interval |
10 |
The max time in seconds between attempts to acquire the advisory lock, the lock is attempted to be acquired using an exponential backoff algorithm. |
dbname |
DatabaseName |
The name of the database to connect to |
user |
The user to sign in as. Can be omitted | |
password |
The user's password. Can be omitted | |
host |
The host to connect to | |
port |
The port to bind to |
File: database/mysql/README.md
MySQL
mysql://user:password@tcp(host:port)/dbname?query
| URL Query | WithInstance Config | Description |
|---|---|---|
x-migrations-table |
MigrationsTable |
Name of the migrations table |
x-no-lock |
NoLock |
Set to true to skip GET_LOCK/RELEASE_LOCK statements. Useful for multi-master MySQL flavors. Only run migrations from one host when this is enabled. |
x-statement-timeout |
StatementTimeout |
Abort any statement that takes more than the specified number of milliseconds, functionally similar to Server-side SELECT statement timeouts but enforced by the client. Available for all versions of MySQL, not just >=5.7. |
dbname |
DatabaseName |
The name of the database to connect to |
user |
The user to sign in as | |
password |
The user's password | |
host |
The host to connect to. | |
port |
The port to bind to. | |
tls |
TLS / SSL encrypted connection parameter; see go-sql-driver. Use any name (e.g. migrate) if you want to use a custom TLS config (x-tls- queries). |
|
x-tls-ca |
The location of the CA (certificate authority) file. | |
x-tls-cert |
The location of the client certificate file. Must be used with x-tls-key. |
|
x-tls-key |
The location of the private key file. Must be used with x-tls-cert. |
|
x-tls-insecure-skip-verify |
Whether or not to use SSL (true|false) |
Use with existing client
If you use the MySQL driver with existing database client, you must create the client with parameter multiStatements=true:
package main
import (
"database/sql"
_ "github.com/go-sql-driver/mysql"
"github.com/golang-migrate/migrate/v4"
"github.com/golang-migrate/migrate/v4/database/mysql"
_ "github.com/golang-migrate/migrate/v4/source/file"
)
func main() {
db, _ := sql.Open("mysql", "user:password@tcp(host:port)/dbname?multiStatements=true")
driver, _ := mysql.WithInstance(db, &mysql.Config{})
m, _ := migrate.NewWithDatabaseInstance(
"file:///migrations",
"mysql",
driver,
)
m.Steps(2)
}Upgrading from v1
- Write down the current migration version from schema_migrations
DROP TABLE schema_migrations- Wrap your existing migrations in transactions (BEGIN/COMMIT) if you use multiple statements within one migration.
- Download and install the latest migrate version.
- Force the current migration version with
migrate force <current_version>.