119 lines
4.1 KiB
Go
119 lines
4.1 KiB
Go
// Package dbutil provides a simple framework for in-process database
|
|
// migrations. You provide the SQL files and they are run to upgrade
|
|
// the database. A versions table is automatically created in the
|
|
// database to track which migrations have been applied. There is
|
|
// support for multiple migration pathways, for example v0->v2 versus
|
|
// v0->v1->v2, and the shorter one is prioritized if both are
|
|
// provided.
|
|
//
|
|
// Example usage from Go:
|
|
//
|
|
// package main
|
|
//
|
|
// import (
|
|
// "context"
|
|
// "database/sql"
|
|
// "embed"
|
|
//
|
|
// "go.mau.fi/util/dbutil"
|
|
// )
|
|
//
|
|
// //go:embed *.sql
|
|
// var upgrades embed.FS
|
|
//
|
|
// func mainE() error {
|
|
// ctx := context.Background()
|
|
// rawDB, err := sql.Open("sqlite3", "./hotdogs.db")
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
// db, err := dbutil.NewWithDB(rawDB, "sqlite3")
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
// table := dbutil.UpgradeTable{}
|
|
// table.RegisterFS(upgrades)
|
|
// err = db.Upgrade(ctx)
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
// // db has been upgraded to latest version
|
|
// return nil
|
|
// }
|
|
//
|
|
// In dbutil, the database is understood to have a monotonic integer
|
|
// sequence of versions starting at v0, v1, v2, etc. By providing
|
|
// migrations you define a directed acyclic graph (DAG) that allows
|
|
// dbutil to find a path from the current recorded database version to
|
|
// the latest version available.
|
|
//
|
|
// Each SQL migration file has a mandatory comment header that
|
|
// identifies which database versions it upgrades between. For example
|
|
// this is a migration that upgrades from v0 to v2:
|
|
//
|
|
// -- v0 -> v2: Do some things
|
|
//
|
|
// You can omit the first version for the common case of upgrading to
|
|
// a version from the previous version. For example this is a
|
|
// migration that upgrades from v1 to v2:
|
|
//
|
|
// -- v2: Do fewer things
|
|
//
|
|
// By providing "v1" and "v2" migrations, a v0 database would be
|
|
// upgraded to v1 and then v2, while by providing an additional "v0 ->
|
|
// v2" migration a v0 database would be upgraded directly to v2 as it
|
|
// is a more direct path. With that migration provided the "v1"
|
|
// migration is no longer needed.
|
|
//
|
|
// By default, when running migrations, if a more recent database
|
|
// version is live than the current code knows about (for example,
|
|
// from running a previous version of the application), dbutil will
|
|
// error out. However, many database migrations are backwards
|
|
// compatible. You can therefore indicate this when writing a
|
|
// migration, and previous versions of the application will accept a
|
|
// database with that migration applied, even if they are unaware of
|
|
// its contents. For example, if the migration from v1 to v2 was
|
|
// backwards compatible, you could provide this migration:
|
|
//
|
|
// -- v2 (compatible with v1+): Do fewer things
|
|
//
|
|
// When applying the migration, the compatibility level (v1) is saved
|
|
// to the versions table in the database, so that older versions of
|
|
// the application which only know about v1 will see that v2 of the
|
|
// database is still OK to use. If the compatibility level is not set,
|
|
// then it defaults to the same as the target version for the
|
|
// migration, which achieves the default behavior described in the
|
|
// previous paragraph.
|
|
//
|
|
// You can provide additional flags immediately following the header
|
|
// line. To disable wrapping the upgrade in a single transaction, put
|
|
// "transaction: off" on the second line.
|
|
//
|
|
// -- v5: Upgrade without transaction
|
|
// -- transaction: off
|
|
// // do dangerous stuff
|
|
//
|
|
// Within migrations, there is special syntax that can be used to
|
|
// filter parts of the SQL to apply only with specific dialects. To
|
|
// limit the next line to one dialect:
|
|
//
|
|
// -- only: postgres
|
|
//
|
|
// To limit the next N lines:
|
|
//
|
|
// -- only: sqlite for next 123 lines
|
|
//
|
|
// To limit a block of code, fenced by another directive:
|
|
//
|
|
// -- only: sqlite until "end only"
|
|
// QUERY;
|
|
// ANOTHER QUERY;
|
|
// -- end only sqlite
|
|
//
|
|
// If the single-line limit is on the second line of the file, the
|
|
// whole file is limited to that dialect.
|
|
//
|
|
// If the filter ends with `(lines commented)`, then ALL lines chosen
|
|
// by the filter will be uncommented. The `--` comment prefix must be
|
|
// at the beginning of the line with no whitespace ahead of it.
|
|
package dbutil
|