Documentation ¶
Index ¶
- Constants
- func IDFromFilename(filename string) string
- func SortByID(migrations []Migration)
- type AppliedMigration
- func Applied(ctx context.Context, db *sql.DB, dir fs.FS, logger Logger) ([]AppliedMigration, error)
- func MarkAllApplied(ctx context.Context, db *sql.DB, dir fs.FS, logger Logger) ([]AppliedMigration, error)
- func MarkAllUnapplied(ctx context.Context, db *sql.DB, dir fs.FS, logger Logger) ([]AppliedMigration, error)
- func MarkApplied(ctx context.Context, db *sql.DB, dir fs.FS, logger Logger, ids ...string) ([]AppliedMigration, error)
- func MarkUnapplied(ctx context.Context, db *sql.DB, dir fs.FS, logger Logger, ids ...string) ([]AppliedMigration, error)
- func RecalculateAllChecksums(ctx context.Context, db *sql.DB, dir fs.FS, logger Logger) ([]AppliedMigration, error)
- func RecalculateChecksums(ctx context.Context, db *sql.DB, dir fs.FS, logger Logger, ids ...string) ([]AppliedMigration, error)
- func SetChecksums(ctx context.Context, db *sql.DB, dir fs.FS, logger Logger, ...) ([]AppliedMigration, error)
- type ChecksumUpdate
- type Executor
- type Helper
- type LogField
- type LogLevel
- type Logger
- type Migration
- type Migrator
- func (m *Migrator) Applied(ctx context.Context, db Executor) ([]AppliedMigration, error)
- func (m *Migrator) MarkAllApplied(ctx context.Context, db Executor) ([]AppliedMigration, error)
- func (m *Migrator) MarkAllUnapplied(ctx context.Context, db Executor) ([]AppliedMigration, error)
- func (m *Migrator) MarkApplied(ctx context.Context, db Executor, ids ...string) ([]AppliedMigration, error)
- func (m *Migrator) MarkUnapplied(ctx context.Context, db Executor, ids ...string) ([]AppliedMigration, error)
- func (m *Migrator) Migrate(ctx context.Context, db *sql.DB) ([]VerificationError, error)
- func (m *Migrator) Plan(ctx context.Context, db Executor) ([]Migration, error)
- func (m *Migrator) RecalculateAllChecksums(ctx context.Context, db Executor) ([]AppliedMigration, error)
- func (m *Migrator) RecalculateChecksums(ctx context.Context, db Executor, ids ...string) ([]AppliedMigration, error)
- func (m *Migrator) SetChecksums(ctx context.Context, db Executor, updates ...ChecksumUpdate) ([]AppliedMigration, error)
- func (m *Migrator) Verify(ctx context.Context, db Executor) ([]VerificationError, error)
- type TestLogger
- type VerificationError
Constants ¶
const ( // DefaultTableName is the default name of the migrations table that // pgmigrate will use to store a record of applied migrations. DefaultTableName string = "pgmigrate_migrations" )
Variables ¶
This section is empty.
Functions ¶
func IDFromFilename ¶
IDFromFilename removes directory paths and extensions from the filename to return just the filename (no extension).
Examples:
"0001_initial" == IDFromFilename("0001_initial.sql") "0002_whatever.up" == IDFromFilename("0002_whatever.up.sql")
Types ¶
type AppliedMigration ¶
type AppliedMigration struct { Migration Checksum string // The MD5 hash of the SQL of this migration ExecutionTimeInMillis int64 // How long it took to run this migration AppliedAt time.Time // When the migration was run }
AppliedMigration represents a successfully-executed Migration. It embeds the Migration, and adds fields for execution results.
func Applied ¶
Applied returns a list of [AppliedMigration]s in the order that they were applied in (applied_at ASC, id ASC).
If there are no applied migrations, or the specified table does not exist, this will return an empty list without an error.
func MarkAllApplied ¶ added in v0.0.2
func MarkAllApplied(ctx context.Context, db *sql.DB, dir fs.FS, logger Logger) ([]AppliedMigration, error)
MarkAllApplied (⚠️ danger) is a manual operation that marks all known migrations as applied without running them.
You should NOT use this as part of normal operations, it exists to help devops/db-admin/sres interact with migration state.
It returns a list of the [AppliedMigration]s that have been marked as applied.
func MarkAllUnapplied ¶ added in v0.0.2
func MarkAllUnapplied(ctx context.Context, db *sql.DB, dir fs.FS, logger Logger) ([]AppliedMigration, error)
MarkAllUnapplied (⚠️ danger) is a manual operation that marks all known migrations as unapplied (not having been run) by removing their records from the migrations table.
You should NOT use this as part of normal operations, it exists to help devops/db-admin/sres interact with migration state.
It returns a list of the [AppliedMigration]s that have been marked as unapplied.
func MarkApplied ¶ added in v0.0.2
func MarkApplied(ctx context.Context, db *sql.DB, dir fs.FS, logger Logger, ids ...string) ([]AppliedMigration, error)
MarkApplied (⚠️ danger) is a manual operation that marks specific migrations as applied without running them.
You should NOT use this as part of normal operations, it exists to help devops/db-admin/sres interact with migration state.
It returns a list of the [AppliedMigration]s that have been marked as applied.
func MarkUnapplied ¶ added in v0.0.2
func MarkUnapplied(ctx context.Context, db *sql.DB, dir fs.FS, logger Logger, ids ...string) ([]AppliedMigration, error)
MarkUnapplied (⚠️ danger) is a manual operation that marks specific migrations as unapplied (not having been run) by removing their records from the migrations table.
You should NOT use this as part of normal operations, it exists to help devops/db-admin/sres interact with migration state.
It returns a list of the [AppliedMigration]s that have been marked as unapplied.
func RecalculateAllChecksums ¶ added in v0.0.2
func RecalculateAllChecksums(ctx context.Context, db *sql.DB, dir fs.FS, logger Logger) ([]AppliedMigration, error)
RecalculateChecksums (⚠️ danger) is a manual operation that explicitly recalculates the checksums of all known migrations and updates their records in the migrations table to have the calculated checksum.
You should NOT use this as part of normal operations, it exists to help devops/db-admin/sres interact with migration state.
It returns a list of the [AppliedMigration]s whose checksums have been recalculated.
func RecalculateChecksums ¶ added in v0.0.2
func RecalculateChecksums(ctx context.Context, db *sql.DB, dir fs.FS, logger Logger, ids ...string) ([]AppliedMigration, error)
RecalculateChecksums (⚠️ danger) is a manual operation that explicitly recalculates the checksums of the specified migrations and updates their records in the migrations table to have the calculated checksum.
You should NOT use this as part of normal operations, it exists to help devops/db-admin/sres interact with migration state.
It returns a list of the [AppliedMigration]s whose checksums have been recalculated.
func SetChecksums ¶ added in v0.0.2
func SetChecksums(ctx context.Context, db *sql.DB, dir fs.FS, logger Logger, updates ...ChecksumUpdate) ([]AppliedMigration, error)
SetChecksums (⚠️ danger) is a manual operation that explicitly sets the recorded checksum of applied migrations in the migrations table.
You should NOT use this as part of normal operations, it exists to help devops/db-admin/sres interact with migration state.
It returns a list of the [AppliedMigration]s whose checksums have been updated.
type ChecksumUpdate ¶ added in v0.0.2
type ChecksumUpdate struct { MigrationID string // The ID of the migration to update, `0001_initial` NewChecksum string // The checksum to set in the migrations table, `aaaabbbbccccdddd` }
ChecksumUpdate represents an update to a specific migration. This struct is used instead of a `map[migrationID]checksum“ in order to apply multiple updates in a consistent order.
type Executor ¶
type Executor interface { ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error) QueryRowContext(ctx context.Context, query string, args ...any) *sql.Row QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error) BeginTx(ctx context.Context, opts *sql.TxOptions) (*sql.Tx, error) }
Executor is satisfied by *sql.DB as well as *sql.Conn. Many of the Migrator's methods are designed to work inside of a session-scoped lock, which requires running queries on a *sql.Conn. These methods accept an Executor so that they can more easily be used by an external caller.
type Helper ¶ added in v0.0.2
type Helper interface {
Helper()
}
Helper is an optional interface that your logger can implement to help make debugging and stacktraces easier to understand, primarily in tests. If a Logger implements this interface, pgmigrate will call Helper() in its own helper methods for writing to your logger, with the goal of omitting pgmigrate's helper methods from your stacktraces.
For instance, the TestLogger we provide embeds a testing.T, which implements Helper().
You do *not* need to implement this interface in order for pgmigrate to successfully use your logger.
type LogLevel ¶ added in v0.0.2
type LogLevel string
LogLevel represents the severity of the log message, and is one of
type Logger ¶ added in v0.0.2
Logger is a generic logging interface so that you can easily use pgmigrate with your existing structured ogging solution -- hopefully it is not difficult for you to write an adapter.
type Migration ¶
type Migration struct { ID string // the filename of the migration, without the .sql extension SQL string // the contents of the migration file }
Migration represents a single SQL migration.
func Load ¶
Load walks a filesystem from its root and extracts all files ending in `.sql` as Migrations, with the filename (without extension) being the ID and the file's contents being the SQL.
From disk:
// the migration files will be read at run time fs := os.DirFS("./path/to/migrations/directory/*.sql")
From an embedded fs:
// the migration files will be embedded at compile time //go:embed path/to/migrations/directory/*.sql var fs embed.FS
Load returns the migrations in sorted order.
func Plan ¶
Plan shows which migrations, if any, would be applied, in the order that they would be applied in.
The plan will be a list of [Migration]s that are present in the migrations directory that have not yet been marked as applied in the migrations table.
The migrations in the plan will be ordered by their IDs, in ascending lexicographical order. This is the same order that you see if you use "ls". This is also the same order that they will be applied in.
The ID of a migration is its filename without the ".sql" suffix.
A migration will only ever be applied once. Editing the contents of the migration file will NOT result in it being re-applied. Instead, you will see a verification error warning that the contents of the migration differ from its contents when it was previously applied.
Migrations can be applied "out of order". For instance, if there were three migrations that had been applied:
- 001_initial
- 002_create_users
- 003_create_viewers
And a new migration "002_create_companies" is merged:
- 001_initial
- 002_create_companies
- 002_create_users
- 003_create_viewers
Running "pgmigrate plan" will show:
- 002_create_companies
Because the other migrations have already been applied. This is by design; most of the time, when you're working with your coworkers, you will not write migrations that conflict with each other. As long as you use a migration name/number higher than that of any dependencies, you will not have any problems.
func (*Migration) MD5 ¶
MD5 computes the MD5 hash of the SQL for this migration so that it can be uniquely identified. After a Migration is applied, the AppliedMigration will store this hash in the `Checksum` field.
type Migrator ¶
type Migrator struct { // Migrations is the full set of migrations that describe the desired state // of the database. Migrations []Migration // Logger is used by the Migrator to log messages as it operates. It is // designed to be easy to adapt to whatever logging system you use. // // [NewMigrator] defaults it to `nil`, which will prevent any messages from // being logged. Logger Logger // TableName is the table that this migrator should use to keep track of // applied migrations. // // [NewMigrator] defaults it to [DefaultTableName]. TableName string }
Migrator should be instantiated with NewMigrator rather than used directly. It contains the state necessary to perform migrations-related operations.
func NewMigrator ¶
NewMigrator creates a Migrator and sets appropriate default values for all configurable fields:
- Logger: `nil`, no messages will be logged
- TableName: DefaultTableName
To configure these fields, just set the values on the struct.
func (*Migrator) Applied ¶
Applied returns a list of [AppliedMigration]s in the order that they were applied in (applied_at ASC, id ASC).
If there are no applied migrations, or the specified table does not exist, this will return an empty list without an error.
func (*Migrator) MarkAllApplied ¶ added in v0.0.2
MarkAllApplied (⚠️ danger) is a manual operation that marks all known migrations as applied without running them.
You should NOT use this as part of normal operations, it exists to help devops/db-admin/sres interact with migration state.
It returns a list of the [AppliedMigration]s that have been marked as applied.
func (*Migrator) MarkAllUnapplied ¶ added in v0.0.2
MarkAllUnapplied (⚠️ danger) is a manual operation that marks all known migrations as unapplied (not having been run) by removing their records from the migrations table.
You should NOT use this as part of normal operations, it exists to help devops/db-admin/sres interact with migration state.
It returns a list of the [AppliedMigration]s that have been marked as unapplied.
func (*Migrator) MarkApplied ¶ added in v0.0.2
func (m *Migrator) MarkApplied(ctx context.Context, db Executor, ids ...string) ([]AppliedMigration, error)
MarkApplied (⚠️ danger) is a manual operation that marks specific migrations as applied without running them.
You should NOT use this as part of normal operations, it exists to help devops/db-admin/sres interact with migration state.
It returns a list of the [AppliedMigration]s that have been marked as applied.
func (*Migrator) MarkUnapplied ¶ added in v0.0.2
func (m *Migrator) MarkUnapplied(ctx context.Context, db Executor, ids ...string) ([]AppliedMigration, error)
MarkUnapplied (⚠️ danger) is a manual operation that marks specific migrations as unapplied (not having been run) by removing their records from the migrations table.
You should NOT use this as part of normal operations, it exists to help devops/db-admin/sres interact with migration state.
It returns a list of the [AppliedMigration]s that have been marked as unapplied.
func (*Migrator) Migrate ¶
Migrate will apply any previously applied migrations. It stores metadata in the database with the following schema:
- id: text not null
- checksum: text not null
- execution_time_in_millis: integer not null
- applied_at: timestamp with time zone not null
It does the following things:
First, acquire an advisory lock to prevent conflicts with other instances that may be running in parallel. This way only one migrator will attempt to run the migrations at any point in time.
Then, calculate a plan of migrations to apply. The plan will be a list of migrations that have not yet been marked as applied in the migrations table. The migrations in the plan will be ordered by their IDs, in ascending lexicographical order.
For each migration in the plan,
- Begin a transaction
- Run the migration
- Create a record in the migrations table saying that the migration was applied
- Commit the transaction
If a migration fails at any point, the transaction will roll back. A failed migration results in NO record for that migration in the migrations table, which means that future attempts to run the migrations will include it in their plan.
Migrate() will immediately return the error related to a failed migration, and will NOT attempt to run any further migrations. Any migrations applied before the failure will remain applied. Any migrations not yet applied will not be attempted.
If all the migrations in the plan are applied successfully, then call Verify() to double-check that all known migrations have been marked as applied in the migrations table.
Finally, the advisory lock is released.
func (*Migrator) Plan ¶
Plan shows which migrations, if any, would be applied, in the order that they would be applied in.
The plan will be a list of [Migration]s that are present in the migrations directory that have not yet been marked as applied in the migrations table.
The migrations in the plan will be ordered by their IDs, in ascending lexicographical order. This is the same order that you see if you use "ls". This is also the same order that they will be applied in.
The ID of a migration is its filename without the ".sql" suffix.
A migration will only ever be applied once. Editing the contents of the migration file will NOT result in it being re-applied. Instead, you will see a verification error warning that the contents of the migration differ from its contents when it was previously applied.
Migrations can be applied "out of order". For instance, if there were three migrations that had been applied:
- 001_initial
- 002_create_users
- 003_create_viewers
And a new migration "002_create_companies" is merged:
- 001_initial
- 002_create_companies
- 002_create_users
- 003_create_viewers
Running "pgmigrate plan" will show:
- 002_create_companies
Because the other migrations have already been applied. This is by design; most of the time, when you're working with your coworkers, you will not write migrations that conflict with each other. As long as you use a migration name/number higher than that of any dependencies, you will not have any problems.
func (*Migrator) RecalculateAllChecksums ¶ added in v0.0.2
func (*Migrator) RecalculateChecksums ¶ added in v0.0.2
func (m *Migrator) RecalculateChecksums(ctx context.Context, db Executor, ids ...string) ([]AppliedMigration, error)
RecalculateChecksums (⚠️ danger) is a manual operation that explicitly recalculates the checksums of the specified migrations and updates their records in the migrations table to have the calculated checksum.
You should NOT use this as part of normal operations, it exists to help devops/db-admin/sres interact with migration state.
It returns a list of the [AppliedMigration]s whose checksums have been recalculated.
func (*Migrator) SetChecksums ¶ added in v0.0.2
func (m *Migrator) SetChecksums(ctx context.Context, db Executor, updates ...ChecksumUpdate) ([]AppliedMigration, error)
SetChecksums (⚠️ danger) is a manual operation that explicitly sets the recorded checksum of applied migrations in the migrations table.
You should NOT use this as part of normal operations, it exists to help devops/db-admin/sres interact with migration state.
It returns a list of the [AppliedMigration]s whose checksums have been updated.
func (*Migrator) Verify ¶
Verify returns a list of [VerificationError]s with warnings for any migrations that:
- Are marked as applied in the database table but do not exist in the migrations directory.
- Have a different checksum in the database than the current file hash.
These warnings usually signify that the schema described by the migrations no longer matches the schema in the database. Usually the cause is removing/editing a migration without realizing that it was already applied to a database.
The most common cause of a warning is in the case that a new release/deployment contains migrations, the migrations are applied successfully, but the release is then rolled back due to other issues. In this case the warning is just that, a warning, and should not be a long-term problem.
These warnings should not prevent your application from starting, but are worth showing to a human devops/db-admin/sre-type person for them to investigate.
type TestLogger ¶ added in v0.0.2
TestLogger implements the Logger and Helper interface and writes all logs to a given test's output in such a way that stack traces are correctly preserved.
func NewTestLogger ¶ added in v0.0.2
func NewTestLogger(t *testing.T) TestLogger
NewTestLogger returns a TestLogger, which is a Logger and Helper (due to the embedded testing.T) that writes all logs to a given test's output in such a way that stack traces are correctly preserved.
type VerificationError ¶
A VerificationError represents a warning of either of two types:
- a migration is marked as applied to the database but is not present in the directory of migrations: this can happen if a migration is applied, but the code containing that migration is later rolled back.
- a migration whose hash (when applied) doesn't match its current hash (when calculated from its SQL contents): this can happen if someone edits a migration after it was previously applied.
These verification errors are worth looking into, but should not be treated the same as a failure to apply migrations. Typically these are warned or alerted on by the app using this migration library, and results in a human intervening in some way.
func Migrate ¶
func Migrate(ctx context.Context, db *sql.DB, dir fs.FS, logger Logger) ([]VerificationError, error)
Migrate will apply any previously applied migrations. It stores metadata in the DefaultTableName table, with the following schema: - id: text not null - checksum: text not null - execution_time_in_millis: integer not null - applied_at: timestamp with time zone not null
It does the following things:
First, acquire an advisory lock to prevent conflicts with other instances that may be running in parallel. This way only one migrator will attempt to run the migrations at any point in time.
Then, calculate a plan of migrations to apply. The plan will be a list of migrations that have not yet been marked as applied in the migrations table. The migrations in the plan will be ordered by their IDs, in ascending lexicographical order.
For each migration in the plan,
- Begin a transaction
- Run the migration
- Create a record in the migrations table saying that the migration was applied
- Commit the transaction
If a migration fails at any point, the transaction will roll back. A failed migration results in NO record for that migration in the migrations table, which means that future attempts to run the migrations will include it in their plan.
Migrate() will immediately return the error related to a failed migration, and will NOT attempt to run any further migrations. Any migrations applied before the failure will remain applied. Any migrations not yet applied will not be attempted.
If all the migrations in the plan are applied successfully, then call Verify() to double-check that all known migrations have been marked as applied in the migrations table.
Finally, the advisory lock is released.
func Verify ¶
Verify returns a list of [VerificationError]s with warnings for any migrations that:
- Are marked as applied in the database table but do not exist in the migrations directory.
- Have a different checksum in the database than the current file hash.
These warnings usually signify that the schema described by the migrations no longer matches the schema in the database. Usually the cause is removing/editing a migration without realizing that it was already applied to a database.
The most common cause of a warning is in the case that a new release/deployment contains migrations, the migrations are applied successfully, but the release is then rolled back due to other issues. In this case the warning is just that, a warning, and should not be a long-term problem.
These warnings should not prevent your application from starting, but are worth showing to a human devops/db-admin/sre-type person for them to investigate.
Source Files ¶
Directories ¶
Path | Synopsis |
---|---|
cli
module
|
|
cmd
|
|
pgmigrate
Module
|
|
example
module
|
|
internal
|
|
migrations
migrations contains example migration data that is used in tests.
|
migrations contains example migration data that is used in tests. |
multierr
multierr is a reimplementation of https://github.com/uber-go/multierr/ with a much simpler API and zero dependencies.
|
multierr is a reimplementation of https://github.com/uber-go/multierr/ with a much simpler API and zero dependencies. |
sessionlock
sessionlock package provides support for application level distributed locks via advisory locks in PostgreSQL.
|
sessionlock package provides support for application level distributed locks via advisory locks in PostgreSQL. |
withdb
withdb is a simplified way of creating test databases, used to test the internal packages that pgtestdb depends on.
|
withdb is a simplified way of creating test databases, used to test the internal packages that pgtestdb depends on. |