Repository files navigation

Migration

GoDocTests StatusTest Coverage

Simple and pragmatic migrations for Go applications.

Features

  • Super simple driver interface to allow easy implementation for more database/migration drivers.
  • Embeddable migration files.
  • Support for up/down migrations.
  • Atomic migrations (where possible, depending on database support).
  • Support for using Go code as migrations

Drivers

Each driver is implemented in its own module to avoid pulling in unused dependencies into your project.

DriverImport
Apache Phoenixgithub.com/Boostport/migration/driver/phoenix
Go (runs generic go functions)github.com/Boostport/migration/driver/golang
MySQLgithub.com/Boostport/migration/driver/mysql
PostgreSQLgithub.com/Boostport/migration/driver/postgres
SQLitegithub.com/Boostport/migration/driver/sqlite

Quickstart

import (
"github.com/Boostport/migration""github.com/Boostport/migration/driver/mysql"
)
// Create migration source//go:embed migrationsvarembedFS embed.FSembedSource:=&migration.EmbedMigrationSource{
EmbedFS: embedFS,
Dir: "migrations",
}
// Create driverdriver, err:=mysql.New("root:@tcp(localhost)/mydatabase?multiStatements=true")
// Run all up migrationsapplied, err:=migration.Migrate(driver, embedSource, migration.Up, 0)
// Remove the last 2 migrationsapplied, err=migration.Migrate(driver, embedSource, migration.Down, 2)

Writing migrations

Migrations are extremely simple to write:

  • Separate your up and down migrations into different files. For example, 1_init.up.sql and 1_init.down.sql.
  • Prefix your migration with a number or timestamp for versioning: 1_init.up.sql or 1475813115_init.up.sql.
  • The file-extension can be anything you want, but must be present. For example, 1_init.up.sql is valid, but 1_init.up is not,
  • Note: Underscores (_) must be used to separate the number and description in the filename.

Let's say we want to write our first migration to initialize the database.

In that case, we would have a file called 1_init.up.sql containing SQL statements for the up migration:

CREATETABLEtest_data (
id BIGINTNOT NULLPRIMARY KEY,
)

We also create a 1_init.down.sql file containing SQL statements for the down migration:

DROPTABLE IF EXISTS test_data

By default, migrations are run within a transaction. If you do not want a migration to run within a transaction, start the migration file with -- +migration NoTransaction:

-- +migration NoTransactionCREATETABLEtest_data1 (
id BIGINTNOT NULLPRIMARY KEY,
)
CREATETABLEtest_data2 (
id BIGINTNOT NULLPRIMARY KEY,
)

If you would like to create stored procedures, triggers or complex statements that contain semicolns, use BeginStatement and EndStatement to delineate them:

CREATETABLEtest_data1 (
id BIGINTNOT NULLPRIMARY KEY,
)
CREATETABLEtest_data2 (
id BIGINTNOT NULLPRIMARY KEY,
)
-- +migration BeginStatement
CREATE TRIGGER`test_trigger_1`BEFORE UPDATEON`test_data1`FOR EACH ROW BEGININSERT INTO test_data2
SET id =OLD.id;
END
-- +migration EndStatement

Embedding migration files

Using go:embed

This is the recommended method for embedding migration files if you are using Go 1.16+. The go:embed Go's built-in method to embed files into the built binary and does not require any external tools.

Assuming your migration files are in migrations/, initialize a EmbededSource:

//go:embed migrationsvarembedFS embed.FSassetMigration:=&migration.EmbedSource{
EmbedFS: embedFS,
Dir: "migrations",
}

Using Go for migrations

Sometimes, we might be working with a database or have a situation where the query language is not expressive enough to perform the required migrations. For example, we might have to get some data out of the database, perform some transformations and then write it back. For these type of situations, you can use Go for migrations.

When using Go for migrations, create a golang.Source using golang.NewSource(). Then, simply add migrations to the source using the AddMigration() method. You will need to pass in the name of the migration without the extension and direction, e.g. 1_init. For the second parameter, pass in the direction (migration.Up or migration.Down) and for the third parameter, pass in a function or method with this signature: func() error for running the migration.

Finally, you need to define 2 functions:

  • A function for writing or deleting an applied migration matching this signature: func(id string, direction migration.Direction) error
  • A function for getting a list of applied migrations matching this signature: func() ([]string, error)

These are required for initializing the driver:

driver, err:=golang.New(source, updateVersion, applied)

Here's a quick example:

source:=migration.NewGolangMigrationSource()
source.AddMigration("1_init", migration.Up, func() error {
// Run up migration here
})
source.AddMigration("1_init", migration.Down, func() error {
// Run down migration here
})
// Define functionsapplied:=func() ([]string, error) {
// Return list of applied migrations
}
updateVersion:=func(idstring, direction migration.Direction) error {
// Write or delete applied migration in storage
}
// Create driverdriver, err:=golang.New(source, updateVersion, applied)
// Run migrationscount, err=migration.Migrate(driver, source, migration.Up, 0)

TODO (Pull requests welcomed!)

  • Command line program to run migrations
  • More drivers

Why yet another migration library?

We wanted a migration library with the following features:

  • Open to extension for all sorts of databases, not just database/sql drivers or an ORM.
  • Easily embeddable in a Go application.
  • Support for embedding migration files directly into the app.

We narrowed our focus down to 2 contenders: sql-migrate and migrate

sql-migrate leans heavily on the gorp ORM library to perform migrations. Unfortunately, this means that we were restricted to databases supported by gorp. It is easily embeddable in a Go app and supports embedding migration files directly into the Go binary. If database support was a bit more flexible, we would have gone with it.

migrate is highly extensible, and adding support for another database is extremely trivial. However, due to it using the scheme in the dsn to determine which database driver to use, it prevented us from easily implementing an Apache Phoenix driver, which uses the scheme to determine if we should connect over http or https. Due to the way the project is structured, it was also almost impossible to add support for embeddable migration files without major changes.

Contributing

We automatically run some linters using golangci-lint to check code quality before merging it.

You should run and ensure all the checks pass locally before submitting a pull request. The version of golangci-lint to be used is pinned in docker-compose.yml.

To execute the linters:

  1. Install docker.
  2. Execute docker compose run lint.

License

This library is licensed under the Apache 2 License.

About

Simple and pragmatic migrations for Go applications.

Topics

Resources

Stars

73 stars

Watchers

3 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

Migration

GoDocTests StatusTest Coverage

Simple and pragmatic migrations for Go applications.

Features

  • Super simple driver interface to allow easy implementation for more database/migration drivers.
  • Embeddable migration files.
  • Support for up/down migrations.
  • Atomic migrations (where possible, depending on database support).
  • Support for using Go code as migrations

Drivers

Each driver is implemented in its own module to avoid pulling in unused dependencies into your project.

DriverImport
Apache Phoenixgithub.com/Boostport/migration/driver/phoenix
Go (runs generic go functions)github.com/Boostport/migration/driver/golang
MySQLgithub.com/Boostport/migration/driver/mysql
PostgreSQLgithub.com/Boostport/migration/driver/postgres
SQLitegithub.com/Boostport/migration/driver/sqlite

Quickstart

import (
"github.com/Boostport/migration""github.com/Boostport/migration/driver/mysql"
)
// Create migration source//go:embed migrationsvarembedFS embed.FSembedSource:=&migration.EmbedMigrationSource{
EmbedFS: embedFS,
Dir: "migrations",
}
// Create driverdriver, err:=mysql.New("root:@tcp(localhost)/mydatabase?multiStatements=true")
// Run all up migrationsapplied, err:=migration.Migrate(driver, embedSource, migration.Up, 0)
// Remove the last 2 migrationsapplied, err=migration.Migrate(driver, embedSource, migration.Down, 2)

Writing migrations

Migrations are extremely simple to write:

  • Separate your up and down migrations into different files. For example, 1_init.up.sql and 1_init.down.sql.
  • Prefix your migration with a number or timestamp for versioning: 1_init.up.sql or 1475813115_init.up.sql.
  • The file-extension can be anything you want, but must be present. For example, 1_init.up.sql is valid, but 1_init.up is not,
  • Note: Underscores (_) must be used to separate the number and description in the filename.

Let's say we want to write our first migration to initialize the database.

In that case, we would have a file called 1_init.up.sql containing SQL statements for the up migration:

CREATETABLEtest_data (
id BIGINTNOT NULLPRIMARY KEY,
)

We also create a 1_init.down.sql file containing SQL statements for the down migration:

DROPTABLE IF EXISTS test_data

By default, migrations are run within a transaction. If you do not want a migration to run within a transaction, start the migration file with -- +migration NoTransaction:

-- +migration NoTransactionCREATETABLEtest_data1 (
id BIGINTNOT NULLPRIMARY KEY,
)
CREATETABLEtest_data2 (
id BIGINTNOT NULLPRIMARY KEY,
)

If you would like to create stored procedures, triggers or complex statements that contain semicolns, use BeginStatement and EndStatement to delineate them:

CREATETABLEtest_data1 (
id BIGINTNOT NULLPRIMARY KEY,
)
CREATETABLEtest_data2 (
id BIGINTNOT NULLPRIMARY KEY,
)
-- +migration BeginStatement
CREATE TRIGGER`test_trigger_1`BEFORE UPDATEON`test_data1`FOR EACH ROW BEGININSERT INTO test_data2
SET id =OLD.id;
END
-- +migration EndStatement

Embedding migration files

Using go:embed

This is the recommended method for embedding migration files if you are using Go 1.16+. The go:embed Go's built-in method to embed files into the built binary and does not require any external tools.

Assuming your migration files are in migrations/, initialize a EmbededSource:

//go:embed migrationsvarembedFS embed.FSassetMigration:=&migration.EmbedSource{
EmbedFS: embedFS,
Dir: "migrations",
}

Using Go for migrations

Sometimes, we might be working with a database or have a situation where the query language is not expressive enough to perform the required migrations. For example, we might have to get some data out of the database, perform some transformations and then write it back. For these type of situations, you can use Go for migrations.

When using Go for migrations, create a golang.Source using golang.NewSource(). Then, simply add migrations to the source using the AddMigration() method. You will need to pass in the name of the migration without the extension and direction, e.g. 1_init. For the second parameter, pass in the direction (migration.Up or migration.Down) and for the third parameter, pass in a function or method with this signature: func() error for running the migration.

Finally, you need to define 2 functions:

  • A function for writing or deleting an applied migration matching this signature: func(id string, direction migration.Direction) error
  • A function for getting a list of applied migrations matching this signature: func() ([]string, error)

These are required for initializing the driver:

driver, err:=golang.New(source, updateVersion, applied)

Here's a quick example:

source:=migration.NewGolangMigrationSource()
source.AddMigration("1_init", migration.Up, func() error {
// Run up migration here
})
source.AddMigration("1_init", migration.Down, func() error {
// Run down migration here
})
// Define functionsapplied:=func() ([]string, error) {
// Return list of applied migrations
}
updateVersion:=func(idstring, direction migration.Direction) error {
// Write or delete applied migration in storage
}
// Create driverdriver, err:=golang.New(source, updateVersion, applied)
// Run migrationscount, err=migration.Migrate(driver, source, migration.Up, 0)

TODO (Pull requests welcomed!)

  • Command line program to run migrations
  • More drivers

Why yet another migration library?

We wanted a migration library with the following features:

  • Open to extension for all sorts of databases, not just database/sql drivers or an ORM.
  • Easily embeddable in a Go application.
  • Support for embedding migration files directly into the app.

We narrowed our focus down to 2 contenders: sql-migrate and migrate

sql-migrate leans heavily on the gorp ORM library to perform migrations. Unfortunately, this means that we were restricted to databases supported by gorp. It is easily embeddable in a Go app and supports embedding migration files directly into the Go binary. If database support was a bit more flexible, we would have gone with it.

migrate is highly extensible, and adding support for another database is extremely trivial. However, due to it using the scheme in the dsn to determine which database driver to use, it prevented us from easily implementing an Apache Phoenix driver, which uses the scheme to determine if we should connect over http or https. Due to the way the project is structured, it was also almost impossible to add support for embeddable migration files without major changes.

Contributing

We automatically run some linters using golangci-lint to check code quality before merging it.

You should run and ensure all the checks pass locally before submitting a pull request. The version of golangci-lint to be used is pinned in docker-compose.yml.

To execute the linters:

  1. Install docker.
  2. Execute docker compose run lint.

License

This library is licensed under the Apache 2 License.

About

Simple and pragmatic migrations for Go applications.

Topics

Resources

Stars

73 stars

Watchers

3 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Migration

GoDocTests StatusTest Coverage

Simple and pragmatic migrations for Go applications.

Features

  • Super simple driver interface to allow easy implementation for more database/migration drivers.
  • Embeddable migration files.
  • Support for up/down migrations.
  • Atomic migrations (where possible, depending on database support).
  • Support for using Go code as migrations

Drivers

Each driver is implemented in its own module to avoid pulling in unused dependencies into your project.

DriverImport
Apache Phoenixgithub.com/Boostport/migration/driver/phoenix
Go (runs generic go functions)github.com/Boostport/migration/driver/golang
MySQLgithub.com/Boostport/migration/driver/mysql
PostgreSQLgithub.com/Boostport/migration/driver/postgres
SQLitegithub.com/Boostport/migration/driver/sqlite

Quickstart

import (
"github.com/Boostport/migration""github.com/Boostport/migration/driver/mysql"
)
// Create migration source//go:embed migrationsvarembedFS embed.FSembedSource:=&migration.EmbedMigrationSource{
EmbedFS: embedFS,
Dir: "migrations",
}
// Create driverdriver, err:=mysql.New("root:@tcp(localhost)/mydatabase?multiStatements=true")
// Run all up migrationsapplied, err:=migration.Migrate(driver, embedSource, migration.Up, 0)
// Remove the last 2 migrationsapplied, err=migration.Migrate(driver, embedSource, migration.Down, 2)

Writing migrations

Migrations are extremely simple to write:

  • Separate your up and down migrations into different files. For example, 1_init.up.sql and 1_init.down.sql.
  • Prefix your migration with a number or timestamp for versioning: 1_init.up.sql or 1475813115_init.up.sql.
  • The file-extension can be anything you want, but must be present. For example, 1_init.up.sql is valid, but 1_init.up is not,
  • Note: Underscores (_) must be used to separate the number and description in the filename.

Let's say we want to write our first migration to initialize the database.

In that case, we would have a file called 1_init.up.sql containing SQL statements for the up migration:

CREATETABLEtest_data (
id BIGINTNOT NULLPRIMARY KEY,
)

We also create a 1_init.down.sql file containing SQL statements for the down migration:

DROPTABLE IF EXISTS test_data

By default, migrations are run within a transaction. If you do not want a migration to run within a transaction, start the migration file with -- +migration NoTransaction:

-- +migration NoTransactionCREATETABLEtest_data1 (
id BIGINTNOT NULLPRIMARY KEY,
)
CREATETABLEtest_data2 (
id BIGINTNOT NULLPRIMARY KEY,
)

If you would like to create stored procedures, triggers or complex statements that contain semicolns, use BeginStatement and EndStatement to delineate them:

CREATETABLEtest_data1 (
id BIGINTNOT NULLPRIMARY KEY,
)
CREATETABLEtest_data2 (
id BIGINTNOT NULLPRIMARY KEY,
)
-- +migration BeginStatement
CREATE TRIGGER`test_trigger_1`BEFORE UPDATEON`test_data1`FOR EACH ROW BEGININSERT INTO test_data2
SET id =OLD.id;
END
-- +migration EndStatement

Embedding migration files

Using go:embed

This is the recommended method for embedding migration files if you are using Go 1.16+. The go:embed Go's built-in method to embed files into the built binary and does not require any external tools.

Assuming your migration files are in migrations/, initialize a EmbededSource:

//go:embed migrationsvarembedFS embed.FSassetMigration:=&migration.EmbedSource{
EmbedFS: embedFS,
Dir: "migrations",
}

Using Go for migrations

Sometimes, we might be working with a database or have a situation where the query language is not expressive enough to perform the required migrations. For example, we might have to get some data out of the database, perform some transformations and then write it back. For these type of situations, you can use Go for migrations.

When using Go for migrations, create a golang.Source using golang.NewSource(). Then, simply add migrations to the source using the AddMigration() method. You will need to pass in the name of the migration without the extension and direction, e.g. 1_init. For the second parameter, pass in the direction (migration.Up or migration.Down) and for the third parameter, pass in a function or method with this signature: func() error for running the migration.

Finally, you need to define 2 functions:

  • A function for writing or deleting an applied migration matching this signature: func(id string, direction migration.Direction) error
  • A function for getting a list of applied migrations matching this signature: func() ([]string, error)

These are required for initializing the driver:

driver, err:=golang.New(source, updateVersion, applied)

Here's a quick example:

source:=migration.NewGolangMigrationSource()
source.AddMigration("1_init", migration.Up, func() error {
// Run up migration here
})
source.AddMigration("1_init", migration.Down, func() error {
// Run down migration here
})
// Define functionsapplied:=func() ([]string, error) {
// Return list of applied migrations
}
updateVersion:=func(idstring, direction migration.Direction) error {
// Write or delete applied migration in storage
}
// Create driverdriver, err:=golang.New(source, updateVersion, applied)
// Run migrationscount, err=migration.Migrate(driver, source, migration.Up, 0)

TODO (Pull requests welcomed!)

  • Command line program to run migrations
  • More drivers

Why yet another migration library?

We wanted a migration library with the following features:

  • Open to extension for all sorts of databases, not just database/sql drivers or an ORM.
  • Easily embeddable in a Go application.
  • Support for embedding migration files directly into the app.

We narrowed our focus down to 2 contenders: sql-migrate and migrate

sql-migrate leans heavily on the gorp ORM library to perform migrations. Unfortunately, this means that we were restricted to databases supported by gorp. It is easily embeddable in a Go app and supports embedding migration files directly into the Go binary. If database support was a bit more flexible, we would have gone with it.

migrate is highly extensible, and adding support for another database is extremely trivial. However, due to it using the scheme in the dsn to determine which database driver to use, it prevented us from easily implementing an Apache Phoenix driver, which uses the scheme to determine if we should connect over http or https. Due to the way the project is structured, it was also almost impossible to add support for embeddable migration files without major changes.

Contributing

We automatically run some linters using golangci-lint to check code quality before merging it.

You should run and ensure all the checks pass locally before submitting a pull request. The version of golangci-lint to be used is pinned in docker-compose.yml.

To execute the linters:

  1. Install docker.
  2. Execute docker compose run lint.

License

This library is licensed under the Apache 2 License.

About

Simple and pragmatic migrations for Go applications.

Topics

Resources

Stars

73 stars

Watchers

3 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Migration

GoDocTests StatusTest Coverage

Simple and pragmatic migrations for Go applications.

Features

  • Super simple driver interface to allow easy implementation for more database/migration drivers.
  • Embeddable migration files.
  • Support for up/down migrations.
  • Atomic migrations (where possible, depending on database support).
  • Support for using Go code as migrations

Drivers

Each driver is implemented in its own module to avoid pulling in unused dependencies into your project.

DriverImport
Apache Phoenixgithub.com/Boostport/migration/driver/phoenix
Go (runs generic go functions)github.com/Boostport/migration/driver/golang
MySQLgithub.com/Boostport/migration/driver/mysql
PostgreSQLgithub.com/Boostport/migration/driver/postgres
SQLitegithub.com/Boostport/migration/driver/sqlite

Quickstart

import (
"github.com/Boostport/migration""github.com/Boostport/migration/driver/mysql"
)
// Create migration source//go:embed migrationsvarembedFS embed.FSembedSource:=&migration.EmbedMigrationSource{
EmbedFS: embedFS,
Dir: "migrations",
}
// Create driverdriver, err:=mysql.New("root:@tcp(localhost)/mydatabase?multiStatements=true")
// Run all up migrationsapplied, err:=migration.Migrate(driver, embedSource, migration.Up, 0)
// Remove the last 2 migrationsapplied, err=migration.Migrate(driver, embedSource, migration.Down, 2)

Writing migrations

Migrations are extremely simple to write:

  • Separate your up and down migrations into different files. For example, 1_init.up.sql and 1_init.down.sql.
  • Prefix your migration with a number or timestamp for versioning: 1_init.up.sql or 1475813115_init.up.sql.
  • The file-extension can be anything you want, but must be present. For example, 1_init.up.sql is valid, but 1_init.up is not,
  • Note: Underscores (_) must be used to separate the number and description in the filename.

Let's say we want to write our first migration to initialize the database.

In that case, we would have a file called 1_init.up.sql containing SQL statements for the up migration:

CREATETABLEtest_data (
id BIGINTNOT NULLPRIMARY KEY,
)

We also create a 1_init.down.sql file containing SQL statements for the down migration:

DROPTABLE IF EXISTS test_data

By default, migrations are run within a transaction. If you do not want a migration to run within a transaction, start the migration file with -- +migration NoTransaction:

-- +migration NoTransactionCREATETABLEtest_data1 (
id BIGINTNOT NULLPRIMARY KEY,
)
CREATETABLEtest_data2 (
id BIGINTNOT NULLPRIMARY KEY,
)

If you would like to create stored procedures, triggers or complex statements that contain semicolns, use BeginStatement and EndStatement to delineate them:

CREATETABLEtest_data1 (
id BIGINTNOT NULLPRIMARY KEY,
)
CREATETABLEtest_data2 (
id BIGINTNOT NULLPRIMARY KEY,
)
-- +migration BeginStatement
CREATE TRIGGER`test_trigger_1`BEFORE UPDATEON`test_data1`FOR EACH ROW BEGININSERT INTO test_data2
SET id =OLD.id;
END
-- +migration EndStatement

Embedding migration files

Using go:embed

This is the recommended method for embedding migration files if you are using Go 1.16+. The go:embed Go's built-in method to embed files into the built binary and does not require any external tools.

Assuming your migration files are in migrations/, initialize a EmbededSource:

//go:embed migrationsvarembedFS embed.FSassetMigration:=&migration.EmbedSource{
EmbedFS: embedFS,
Dir: "migrations",
}

Using Go for migrations

Sometimes, we might be working with a database or have a situation where the query language is not expressive enough to perform the required migrations. For example, we might have to get some data out of the database, perform some transformations and then write it back. For these type of situations, you can use Go for migrations.

When using Go for migrations, create a golang.Source using golang.NewSource(). Then, simply add migrations to the source using the AddMigration() method. You will need to pass in the name of the migration without the extension and direction, e.g. 1_init. For the second parameter, pass in the direction (migration.Up or migration.Down) and for the third parameter, pass in a function or method with this signature: func() error for running the migration.

Finally, you need to define 2 functions:

  • A function for writing or deleting an applied migration matching this signature: func(id string, direction migration.Direction) error
  • A function for getting a list of applied migrations matching this signature: func() ([]string, error)

These are required for initializing the driver:

driver, err:=golang.New(source, updateVersion, applied)

Here's a quick example:

source:=migration.NewGolangMigrationSource()
source.AddMigration("1_init", migration.Up, func() error {
// Run up migration here
})
source.AddMigration("1_init", migration.Down, func() error {
// Run down migration here
})
// Define functionsapplied:=func() ([]string, error) {
// Return list of applied migrations
}
updateVersion:=func(idstring, direction migration.Direction) error {
// Write or delete applied migration in storage
}
// Create driverdriver, err:=golang.New(source, updateVersion, applied)
// Run migrationscount, err=migration.Migrate(driver, source, migration.Up, 0)

TODO (Pull requests welcomed!)

  • Command line program to run migrations
  • More drivers

Why yet another migration library?

We wanted a migration library with the following features:

  • Open to extension for all sorts of databases, not just database/sql drivers or an ORM.
  • Easily embeddable in a Go application.
  • Support for embedding migration files directly into the app.

We narrowed our focus down to 2 contenders: sql-migrate and migrate

sql-migrate leans heavily on the gorp ORM library to perform migrations. Unfortunately, this means that we were restricted to databases supported by gorp. It is easily embeddable in a Go app and supports embedding migration files directly into the Go binary. If database support was a bit more flexible, we would have gone with it.

migrate is highly extensible, and adding support for another database is extremely trivial. However, due to it using the scheme in the dsn to determine which database driver to use, it prevented us from easily implementing an Apache Phoenix driver, which uses the scheme to determine if we should connect over http or https. Due to the way the project is structured, it was also almost impossible to add support for embeddable migration files without major changes.

Contributing

We automatically run some linters using golangci-lint to check code quality before merging it.

You should run and ensure all the checks pass locally before submitting a pull request. The version of golangci-lint to be used is pinned in docker-compose.yml.

To execute the linters:

  1. Install docker.
  2. Execute docker compose run lint.

License

This library is licensed under the Apache 2 License.

About

Simple and pragmatic migrations for Go applications.

Topics

Resources

Stars

73 stars

Watchers

3 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

Migration

GoDocTests StatusTest Coverage

Simple and pragmatic migrations for Go applications.

Features

  • Super simple driver interface to allow easy implementation for more database/migration drivers.
  • Embeddable migration files.
  • Support for up/down migrations.
  • Atomic migrations (where possible, depending on database support).
  • Support for using Go code as migrations

Drivers

Each driver is implemented in its own module to avoid pulling in unused dependencies into your project.

DriverImport
Apache Phoenixgithub.com/Boostport/migration/driver/phoenix
Go (runs generic go functions)github.com/Boostport/migration/driver/golang
MySQLgithub.com/Boostport/migration/driver/mysql
PostgreSQLgithub.com/Boostport/migration/driver/postgres
SQLitegithub.com/Boostport/migration/driver/sqlite

Quickstart

import (
"github.com/Boostport/migration""github.com/Boostport/migration/driver/mysql"
)
// Create migration source//go:embed migrationsvarembedFS embed.FSembedSource:=&migration.EmbedMigrationSource{
EmbedFS: embedFS,
Dir: "migrations",
}
// Create driverdriver, err:=mysql.New("root:@tcp(localhost)/mydatabase?multiStatements=true")
// Run all up migrationsapplied, err:=migration.Migrate(driver, embedSource, migration.Up, 0)
// Remove the last 2 migrationsapplied, err=migration.Migrate(driver, embedSource, migration.Down, 2)

Writing migrations

Migrations are extremely simple to write:

  • Separate your up and down migrations into different files. For example, 1_init.up.sql and 1_init.down.sql.
  • Prefix your migration with a number or timestamp for versioning: 1_init.up.sql or 1475813115_init.up.sql.
  • The file-extension can be anything you want, but must be present. For example, 1_init.up.sql is valid, but 1_init.up is not,
  • Note: Underscores (_) must be used to separate the number and description in the filename.

Let's say we want to write our first migration to initialize the database.

In that case, we would have a file called 1_init.up.sql containing SQL statements for the up migration:

CREATETABLEtest_data (
id BIGINTNOT NULLPRIMARY KEY,
)

We also create a 1_init.down.sql file containing SQL statements for the down migration:

DROPTABLE IF EXISTS test_data

By default, migrations are run within a transaction. If you do not want a migration to run within a transaction, start the migration file with -- +migration NoTransaction:

-- +migration NoTransactionCREATETABLEtest_data1 (
id BIGINTNOT NULLPRIMARY KEY,
)
CREATETABLEtest_data2 (
id BIGINTNOT NULLPRIMARY KEY,
)

If you would like to create stored procedures, triggers or complex statements that contain semicolns, use BeginStatement and EndStatement to delineate them:

CREATETABLEtest_data1 (
id BIGINTNOT NULLPRIMARY KEY,
)
CREATETABLEtest_data2 (
id BIGINTNOT NULLPRIMARY KEY,
)
-- +migration BeginStatement
CREATE TRIGGER`test_trigger_1`BEFORE UPDATEON`test_data1`FOR EACH ROW BEGININSERT INTO test_data2
SET id =OLD.id;
END
-- +migration EndStatement

Embedding migration files

Using go:embed

This is the recommended method for embedding migration files if you are using Go 1.16+. The go:embed Go's built-in method to embed files into the built binary and does not require any external tools.

Assuming your migration files are in migrations/, initialize a EmbededSource:

//go:embed migrationsvarembedFS embed.FSassetMigration:=&migration.EmbedSource{
EmbedFS: embedFS,
Dir: "migrations",
}

Using Go for migrations

Sometimes, we might be working with a database or have a situation where the query language is not expressive enough to perform the required migrations. For example, we might have to get some data out of the database, perform some transformations and then write it back. For these type of situations, you can use Go for migrations.

When using Go for migrations, create a golang.Source using golang.NewSource(). Then, simply add migrations to the source using the AddMigration() method. You will need to pass in the name of the migration without the extension and direction, e.g. 1_init. For the second parameter, pass in the direction (migration.Up or migration.Down) and for the third parameter, pass in a function or method with this signature: func() error for running the migration.

Finally, you need to define 2 functions:

  • A function for writing or deleting an applied migration matching this signature: func(id string, direction migration.Direction) error
  • A function for getting a list of applied migrations matching this signature: func() ([]string, error)

These are required for initializing the driver:

driver, err:=golang.New(source, updateVersion, applied)

Here's a quick example:

source:=migration.NewGolangMigrationSource()
source.AddMigration("1_init", migration.Up, func() error {
// Run up migration here
})
source.AddMigration("1_init", migration.Down, func() error {
// Run down migration here
})
// Define functionsapplied:=func() ([]string, error) {
// Return list of applied migrations
}
updateVersion:=func(idstring, direction migration.Direction) error {
// Write or delete applied migration in storage
}
// Create driverdriver, err:=golang.New(source, updateVersion, applied)
// Run migrationscount, err=migration.Migrate(driver, source, migration.Up, 0)

TODO (Pull requests welcomed!)

  • Command line program to run migrations
  • More drivers

Why yet another migration library?

We wanted a migration library with the following features:

  • Open to extension for all sorts of databases, not just database/sql drivers or an ORM.
  • Easily embeddable in a Go application.
  • Support for embedding migration files directly into the app.

We narrowed our focus down to 2 contenders: sql-migrate and migrate

sql-migrate leans heavily on the gorp ORM library to perform migrations. Unfortunately, this means that we were restricted to databases supported by gorp. It is easily embeddable in a Go app and supports embedding migration files directly into the Go binary. If database support was a bit more flexible, we would have gone with it.

migrate is highly extensible, and adding support for another database is extremely trivial. However, due to it using the scheme in the dsn to determine which database driver to use, it prevented us from easily implementing an Apache Phoenix driver, which uses the scheme to determine if we should connect over http or https. Due to the way the project is structured, it was also almost impossible to add support for embeddable migration files without major changes.

Contributing

We automatically run some linters using golangci-lint to check code quality before merging it.

You should run and ensure all the checks pass locally before submitting a pull request. The version of golangci-lint to be used is pinned in docker-compose.yml.

To execute the linters:

  1. Install docker.
  2. Execute docker compose run lint.

License

This library is licensed under the Apache 2 License.

About

Simple and pragmatic migrations for Go applications.

Topics

Resources

Stars

73 stars

Watchers

3 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Migration

GoDocTests StatusTest Coverage

Simple and pragmatic migrations for Go applications.

Features

  • Super simple driver interface to allow easy implementation for more database/migration drivers.
  • Embeddable migration files.
  • Support for up/down migrations.
  • Atomic migrations (where possible, depending on database support).
  • Support for using Go code as migrations

Drivers

Each driver is implemented in its own module to avoid pulling in unused dependencies into your project.

DriverImport
Apache Phoenixgithub.com/Boostport/migration/driver/phoenix
Go (runs generic go functions)github.com/Boostport/migration/driver/golang
MySQLgithub.com/Boostport/migration/driver/mysql
PostgreSQLgithub.com/Boostport/migration/driver/postgres
SQLitegithub.com/Boostport/migration/driver/sqlite

Quickstart

import (
"github.com/Boostport/migration""github.com/Boostport/migration/driver/mysql"
)
// Create migration source//go:embed migrationsvarembedFS embed.FSembedSource:=&migration.EmbedMigrationSource{
EmbedFS: embedFS,
Dir: "migrations",
}
// Create driverdriver, err:=mysql.New("root:@tcp(localhost)/mydatabase?multiStatements=true")
// Run all up migrationsapplied, err:=migration.Migrate(driver, embedSource, migration.Up, 0)
// Remove the last 2 migrationsapplied, err=migration.Migrate(driver, embedSource, migration.Down, 2)

Writing migrations

Migrations are extremely simple to write:

  • Separate your up and down migrations into different files. For example, 1_init.up.sql and 1_init.down.sql.
  • Prefix your migration with a number or timestamp for versioning: 1_init.up.sql or 1475813115_init.up.sql.
  • The file-extension can be anything you want, but must be present. For example, 1_init.up.sql is valid, but 1_init.up is not,
  • Note: Underscores (_) must be used to separate the number and description in the filename.

Let's say we want to write our first migration to initialize the database.

In that case, we would have a file called 1_init.up.sql containing SQL statements for the up migration:

CREATETABLEtest_data (
id BIGINTNOT NULLPRIMARY KEY,
)

We also create a 1_init.down.sql file containing SQL statements for the down migration:

DROPTABLE IF EXISTS test_data

By default, migrations are run within a transaction. If you do not want a migration to run within a transaction, start the migration file with -- +migration NoTransaction:

-- +migration NoTransactionCREATETABLEtest_data1 (
id BIGINTNOT NULLPRIMARY KEY,
)
CREATETABLEtest_data2 (
id BIGINTNOT NULLPRIMARY KEY,
)

If you would like to create stored procedures, triggers or complex statements that contain semicolns, use BeginStatement and EndStatement to delineate them:

CREATETABLEtest_data1 (
id BIGINTNOT NULLPRIMARY KEY,
)
CREATETABLEtest_data2 (
id BIGINTNOT NULLPRIMARY KEY,
)
-- +migration BeginStatement
CREATE TRIGGER`test_trigger_1`BEFORE UPDATEON`test_data1`FOR EACH ROW BEGININSERT INTO test_data2
SET id =OLD.id;
END
-- +migration EndStatement

Embedding migration files

Using go:embed

This is the recommended method for embedding migration files if you are using Go 1.16+. The go:embed Go's built-in method to embed files into the built binary and does not require any external tools.

Assuming your migration files are in migrations/, initialize a EmbededSource:

//go:embed migrationsvarembedFS embed.FSassetMigration:=&migration.EmbedSource{
EmbedFS: embedFS,
Dir: "migrations",
}

Using Go for migrations

Sometimes, we might be working with a database or have a situation where the query language is not expressive enough to perform the required migrations. For example, we might have to get some data out of the database, perform some transformations and then write it back. For these type of situations, you can use Go for migrations.

When using Go for migrations, create a golang.Source using golang.NewSource(). Then, simply add migrations to the source using the AddMigration() method. You will need to pass in the name of the migration without the extension and direction, e.g. 1_init. For the second parameter, pass in the direction (migration.Up or migration.Down) and for the third parameter, pass in a function or method with this signature: func() error for running the migration.

Finally, you need to define 2 functions:

  • A function for writing or deleting an applied migration matching this signature: func(id string, direction migration.Direction) error
  • A function for getting a list of applied migrations matching this signature: func() ([]string, error)

These are required for initializing the driver:

driver, err:=golang.New(source, updateVersion, applied)

Here's a quick example:

source:=migration.NewGolangMigrationSource()
source.AddMigration("1_init", migration.Up, func() error {
// Run up migration here
})
source.AddMigration("1_init", migration.Down, func() error {
// Run down migration here
})
// Define functionsapplied:=func() ([]string, error) {
// Return list of applied migrations
}
updateVersion:=func(idstring, direction migration.Direction) error {
// Write or delete applied migration in storage
}
// Create driverdriver, err:=golang.New(source, updateVersion, applied)
// Run migrationscount, err=migration.Migrate(driver, source, migration.Up, 0)

TODO (Pull requests welcomed!)

  • Command line program to run migrations
  • More drivers

Why yet another migration library?

We wanted a migration library with the following features:

  • Open to extension for all sorts of databases, not just database/sql drivers or an ORM.
  • Easily embeddable in a Go application.
  • Support for embedding migration files directly into the app.

We narrowed our focus down to 2 contenders: sql-migrate and migrate

sql-migrate leans heavily on the gorp ORM library to perform migrations. Unfortunately, this means that we were restricted to databases supported by gorp. It is easily embeddable in a Go app and supports embedding migration files directly into the Go binary. If database support was a bit more flexible, we would have gone with it.

migrate is highly extensible, and adding support for another database is extremely trivial. However, due to it using the scheme in the dsn to determine which database driver to use, it prevented us from easily implementing an Apache Phoenix driver, which uses the scheme to determine if we should connect over http or https. Due to the way the project is structured, it was also almost impossible to add support for embeddable migration files without major changes.

Contributing

We automatically run some linters using golangci-lint to check code quality before merging it.

You should run and ensure all the checks pass locally before submitting a pull request. The version of golangci-lint to be used is pinned in docker-compose.yml.

To execute the linters:

  1. Install docker.
  2. Execute docker compose run lint.

License

This library is licensed under the Apache 2 License.

About

Simple and pragmatic migrations for Go applications.

Topics

Resources

Stars

73 stars

Watchers

3 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Migration

GoDocTests StatusTest Coverage

Simple and pragmatic migrations for Go applications.

Features

  • Super simple driver interface to allow easy implementation for more database/migration drivers.
  • Embeddable migration files.
  • Support for up/down migrations.
  • Atomic migrations (where possible, depending on database support).
  • Support for using Go code as migrations

Drivers

Each driver is implemented in its own module to avoid pulling in unused dependencies into your project.

DriverImport
Apache Phoenixgithub.com/Boostport/migration/driver/phoenix
Go (runs generic go functions)github.com/Boostport/migration/driver/golang
MySQLgithub.com/Boostport/migration/driver/mysql
PostgreSQLgithub.com/Boostport/migration/driver/postgres
SQLitegithub.com/Boostport/migration/driver/sqlite

Quickstart

import (
"github.com/Boostport/migration""github.com/Boostport/migration/driver/mysql"
)
// Create migration source//go:embed migrationsvarembedFS embed.FSembedSource:=&migration.EmbedMigrationSource{
EmbedFS: embedFS,
Dir: "migrations",
}
// Create driverdriver, err:=mysql.New("root:@tcp(localhost)/mydatabase?multiStatements=true")
// Run all up migrationsapplied, err:=migration.Migrate(driver, embedSource, migration.Up, 0)
// Remove the last 2 migrationsapplied, err=migration.Migrate(driver, embedSource, migration.Down, 2)

Writing migrations

Migrations are extremely simple to write:

  • Separate your up and down migrations into different files. For example, 1_init.up.sql and 1_init.down.sql.
  • Prefix your migration with a number or timestamp for versioning: 1_init.up.sql or 1475813115_init.up.sql.
  • The file-extension can be anything you want, but must be present. For example, 1_init.up.sql is valid, but 1_init.up is not,
  • Note: Underscores (_) must be used to separate the number and description in the filename.

Let's say we want to write our first migration to initialize the database.

In that case, we would have a file called 1_init.up.sql containing SQL statements for the up migration:

CREATETABLEtest_data (
id BIGINTNOT NULLPRIMARY KEY,
)

We also create a 1_init.down.sql file containing SQL statements for the down migration:

DROPTABLE IF EXISTS test_data

By default, migrations are run within a transaction. If you do not want a migration to run within a transaction, start the migration file with -- +migration NoTransaction:

-- +migration NoTransactionCREATETABLEtest_data1 (
id BIGINTNOT NULLPRIMARY KEY,
)
CREATETABLEtest_data2 (
id BIGINTNOT NULLPRIMARY KEY,
)

If you would like to create stored procedures, triggers or complex statements that contain semicolns, use BeginStatement and EndStatement to delineate them:

CREATETABLEtest_data1 (
id BIGINTNOT NULLPRIMARY KEY,
)
CREATETABLEtest_data2 (
id BIGINTNOT NULLPRIMARY KEY,
)
-- +migration BeginStatement
CREATE TRIGGER`test_trigger_1`BEFORE UPDATEON`test_data1`FOR EACH ROW BEGININSERT INTO test_data2
SET id =OLD.id;
END
-- +migration EndStatement

Embedding migration files

Using go:embed

This is the recommended method for embedding migration files if you are using Go 1.16+. The go:embed Go's built-in method to embed files into the built binary and does not require any external tools.

Assuming your migration files are in migrations/, initialize a EmbededSource:

//go:embed migrationsvarembedFS embed.FSassetMigration:=&migration.EmbedSource{
EmbedFS: embedFS,
Dir: "migrations",
}

Using Go for migrations

Sometimes, we might be working with a database or have a situation where the query language is not expressive enough to perform the required migrations. For example, we might have to get some data out of the database, perform some transformations and then write it back. For these type of situations, you can use Go for migrations.

When using Go for migrations, create a golang.Source using golang.NewSource(). Then, simply add migrations to the source using the AddMigration() method. You will need to pass in the name of the migration without the extension and direction, e.g. 1_init. For the second parameter, pass in the direction (migration.Up or migration.Down) and for the third parameter, pass in a function or method with this signature: func() error for running the migration.

Finally, you need to define 2 functions:

  • A function for writing or deleting an applied migration matching this signature: func(id string, direction migration.Direction) error
  • A function for getting a list of applied migrations matching this signature: func() ([]string, error)

These are required for initializing the driver:

driver, err:=golang.New(source, updateVersion, applied)

Here's a quick example:

source:=migration.NewGolangMigrationSource()
source.AddMigration("1_init", migration.Up, func() error {
// Run up migration here
})
source.AddMigration("1_init", migration.Down, func() error {
// Run down migration here
})
// Define functionsapplied:=func() ([]string, error) {
// Return list of applied migrations
}
updateVersion:=func(idstring, direction migration.Direction) error {
// Write or delete applied migration in storage
}
// Create driverdriver, err:=golang.New(source, updateVersion, applied)
// Run migrationscount, err=migration.Migrate(driver, source, migration.Up, 0)

TODO (Pull requests welcomed!)

  • Command line program to run migrations
  • More drivers

Why yet another migration library?

We wanted a migration library with the following features:

  • Open to extension for all sorts of databases, not just database/sql drivers or an ORM.
  • Easily embeddable in a Go application.
  • Support for embedding migration files directly into the app.

We narrowed our focus down to 2 contenders: sql-migrate and migrate

sql-migrate leans heavily on the gorp ORM library to perform migrations. Unfortunately, this means that we were restricted to databases supported by gorp. It is easily embeddable in a Go app and supports embedding migration files directly into the Go binary. If database support was a bit more flexible, we would have gone with it.

migrate is highly extensible, and adding support for another database is extremely trivial. However, due to it using the scheme in the dsn to determine which database driver to use, it prevented us from easily implementing an Apache Phoenix driver, which uses the scheme to determine if we should connect over http or https. Due to the way the project is structured, it was also almost impossible to add support for embeddable migration files without major changes.

Contributing

We automatically run some linters using golangci-lint to check code quality before merging it.

You should run and ensure all the checks pass locally before submitting a pull request. The version of golangci-lint to be used is pinned in docker-compose.yml.

To execute the linters:

  1. Install docker.
  2. Execute docker compose run lint.

License

This library is licensed under the Apache 2 License.

About

Simple and pragmatic migrations for Go applications.

Topics

Resources

Stars

73 stars

Watchers

3 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

Migration

GoDocTests StatusTest Coverage

Simple and pragmatic migrations for Go applications.

Features

  • Super simple driver interface to allow easy implementation for more database/migration drivers.
  • Embeddable migration files.
  • Support for up/down migrations.
  • Atomic migrations (where possible, depending on database support).
  • Support for using Go code as migrations

Drivers

Each driver is implemented in its own module to avoid pulling in unused dependencies into your project.

DriverImport
Apache Phoenixgithub.com/Boostport/migration/driver/phoenix
Go (runs generic go functions)github.com/Boostport/migration/driver/golang
MySQLgithub.com/Boostport/migration/driver/mysql
PostgreSQLgithub.com/Boostport/migration/driver/postgres
SQLitegithub.com/Boostport/migration/driver/sqlite

Quickstart

import (
"github.com/Boostport/migration""github.com/Boostport/migration/driver/mysql"
)
// Create migration source//go:embed migrationsvarembedFS embed.FSembedSource:=&migration.EmbedMigrationSource{
EmbedFS: embedFS,
Dir: "migrations",
}
// Create driverdriver, err:=mysql.New("root:@tcp(localhost)/mydatabase?multiStatements=true")
// Run all up migrationsapplied, err:=migration.Migrate(driver, embedSource, migration.Up, 0)
// Remove the last 2 migrationsapplied, err=migration.Migrate(driver, embedSource, migration.Down, 2)

Writing migrations

Migrations are extremely simple to write:

  • Separate your up and down migrations into different files. For example, 1_init.up.sql and 1_init.down.sql.
  • Prefix your migration with a number or timestamp for versioning: 1_init.up.sql or 1475813115_init.up.sql.
  • The file-extension can be anything you want, but must be present. For example, 1_init.up.sql is valid, but 1_init.up is not,
  • Note: Underscores (_) must be used to separate the number and description in the filename.

Let's say we want to write our first migration to initialize the database.

In that case, we would have a file called 1_init.up.sql containing SQL statements for the up migration:

CREATETABLEtest_data (
id BIGINTNOT NULLPRIMARY KEY,
)

We also create a 1_init.down.sql file containing SQL statements for the down migration:

DROPTABLE IF EXISTS test_data

By default, migrations are run within a transaction. If you do not want a migration to run within a transaction, start the migration file with -- +migration NoTransaction:

-- +migration NoTransactionCREATETABLEtest_data1 (
id BIGINTNOT NULLPRIMARY KEY,
)
CREATETABLEtest_data2 (
id BIGINTNOT NULLPRIMARY KEY,
)

If you would like to create stored procedures, triggers or complex statements that contain semicolns, use BeginStatement and EndStatement to delineate them:

CREATETABLEtest_data1 (
id BIGINTNOT NULLPRIMARY KEY,
)
CREATETABLEtest_data2 (
id BIGINTNOT NULLPRIMARY KEY,
)
-- +migration BeginStatement
CREATE TRIGGER`test_trigger_1`BEFORE UPDATEON`test_data1`FOR EACH ROW BEGININSERT INTO test_data2
SET id =OLD.id;
END
-- +migration EndStatement

Embedding migration files

Using go:embed

This is the recommended method for embedding migration files if you are using Go 1.16+. The go:embed Go's built-in method to embed files into the built binary and does not require any external tools.

Assuming your migration files are in migrations/, initialize a EmbededSource:

//go:embed migrationsvarembedFS embed.FSassetMigration:=&migration.EmbedSource{
EmbedFS: embedFS,
Dir: "migrations",
}

Using Go for migrations

Sometimes, we might be working with a database or have a situation where the query language is not expressive enough to perform the required migrations. For example, we might have to get some data out of the database, perform some transformations and then write it back. For these type of situations, you can use Go for migrations.

When using Go for migrations, create a golang.Source using golang.NewSource(). Then, simply add migrations to the source using the AddMigration() method. You will need to pass in the name of the migration without the extension and direction, e.g. 1_init. For the second parameter, pass in the direction (migration.Up or migration.Down) and for the third parameter, pass in a function or method with this signature: func() error for running the migration.

Finally, you need to define 2 functions:

  • A function for writing or deleting an applied migration matching this signature: func(id string, direction migration.Direction) error
  • A function for getting a list of applied migrations matching this signature: func() ([]string, error)

These are required for initializing the driver:

driver, err:=golang.New(source, updateVersion, applied)

Here's a quick example:

source:=migration.NewGolangMigrationSource()
source.AddMigration("1_init", migration.Up, func() error {
// Run up migration here
})
source.AddMigration("1_init", migration.Down, func() error {
// Run down migration here
})
// Define functionsapplied:=func() ([]string, error) {
// Return list of applied migrations
}
updateVersion:=func(idstring, direction migration.Direction) error {
// Write or delete applied migration in storage
}
// Create driverdriver, err:=golang.New(source, updateVersion, applied)
// Run migrationscount, err=migration.Migrate(driver, source, migration.Up, 0)

TODO (Pull requests welcomed!)

  • Command line program to run migrations
  • More drivers

Why yet another migration library?

We wanted a migration library with the following features:

  • Open to extension for all sorts of databases, not just database/sql drivers or an ORM.
  • Easily embeddable in a Go application.
  • Support for embedding migration files directly into the app.

We narrowed our focus down to 2 contenders: sql-migrate and migrate

sql-migrate leans heavily on the gorp ORM library to perform migrations. Unfortunately, this means that we were restricted to databases supported by gorp. It is easily embeddable in a Go app and supports embedding migration files directly into the Go binary. If database support was a bit more flexible, we would have gone with it.

migrate is highly extensible, and adding support for another database is extremely trivial. However, due to it using the scheme in the dsn to determine which database driver to use, it prevented us from easily implementing an Apache Phoenix driver, which uses the scheme to determine if we should connect over http or https. Due to the way the project is structured, it was also almost impossible to add support for embeddable migration files without major changes.

Contributing

We automatically run some linters using golangci-lint to check code quality before merging it.

You should run and ensure all the checks pass locally before submitting a pull request. The version of golangci-lint to be used is pinned in docker-compose.yml.

To execute the linters:

  1. Install docker.
  2. Execute docker compose run lint.

License

This library is licensed under the Apache 2 License.

About

Simple and pragmatic migrations for Go applications.

Topics

Resources

Stars

73 stars

Watchers

3 watching

Forks

Releases

Packages

Used by

Contributors

Languages