gotosocial/vendor/github.com/uptrace/bun/migrate/operations.go

341 lines
9.3 KiB
Go

package migrate
import (
"fmt"
"github.com/uptrace/bun/migrate/sqlschema"
)
// Operation encapsulates the request to change a database definition
// and knowns which operation can revert it.
//
// It is useful to define "monolith" Operations whenever possible,
// even though they a dialect may require several distinct steps to apply them.
// For example, changing a primary key involves first dropping the old constraint
// before generating the new one. Yet, this is only an implementation detail and
// passing a higher-level ChangePrimaryKeyOp will give the dialect more information
// about the applied change.
//
// Some operations might be irreversible due to technical limitations. Returning
// a *comment from GetReverse() will add an explanatory note to the generate migation file.
//
// To declare dependency on another Operation, operations should implement
// { DependsOn(Operation) bool } interface, which Changeset will use to resolve dependencies.
type Operation interface {
GetReverse() Operation
}
// CreateTableOp creates a new table in the schema.
//
// It does not report dependency on any other migration and may be executed first.
// Make sure the dialect does not include FOREIGN KEY constraints in the CREATE TABLE
// statement, as those may potentially reference not-yet-existing columns/tables.
type CreateTableOp struct {
TableName string
Model interface{}
}
var _ Operation = (*CreateTableOp)(nil)
func (op *CreateTableOp) GetReverse() Operation {
return &DropTableOp{TableName: op.TableName}
}
// DropTableOp drops a database table. This operation is not reversible.
type DropTableOp struct {
TableName string
}
var _ Operation = (*DropTableOp)(nil)
func (op *DropTableOp) DependsOn(another Operation) bool {
drop, ok := another.(*DropForeignKeyOp)
return ok && drop.ForeignKey.DependsOnTable(op.TableName)
}
// GetReverse for a DropTable returns a no-op migration. Logically, CreateTable is the reverse,
// but DropTable does not have the table's definition to create one.
func (op *DropTableOp) GetReverse() Operation {
c := comment(fmt.Sprintf("WARNING: \"DROP TABLE %s\" cannot be reversed automatically because table definition is not available", op.TableName))
return &c
}
// RenameTableOp renames the table. Changing the "schema" part of the table's FQN (moving tables between schemas) is not allowed.
type RenameTableOp struct {
TableName string
NewName string
}
var _ Operation = (*RenameTableOp)(nil)
func (op *RenameTableOp) GetReverse() Operation {
return &RenameTableOp{
TableName: op.NewName,
NewName: op.TableName,
}
}
// RenameColumnOp renames a column in the table. If the changeset includes a rename operation
// for the column's table, it should be executed first.
type RenameColumnOp struct {
TableName string
OldName string
NewName string
}
var _ Operation = (*RenameColumnOp)(nil)
func (op *RenameColumnOp) GetReverse() Operation {
return &RenameColumnOp{
TableName: op.TableName,
OldName: op.NewName,
NewName: op.OldName,
}
}
func (op *RenameColumnOp) DependsOn(another Operation) bool {
rename, ok := another.(*RenameTableOp)
return ok && op.TableName == rename.NewName
}
// AddColumnOp adds a new column to the table.
type AddColumnOp struct {
TableName string
ColumnName string
Column sqlschema.Column
}
var _ Operation = (*AddColumnOp)(nil)
func (op *AddColumnOp) GetReverse() Operation {
return &DropColumnOp{
TableName: op.TableName,
ColumnName: op.ColumnName,
Column: op.Column,
}
}
// DropColumnOp drop a column from the table.
//
// While some dialects allow DROP CASCADE to drop dependent constraints,
// explicit handling on constraints is preferred for transparency and debugging.
// DropColumnOp depends on DropForeignKeyOp, DropPrimaryKeyOp, and ChangePrimaryKeyOp
// if any of the constraints is defined on this table.
type DropColumnOp struct {
TableName string
ColumnName string
Column sqlschema.Column
}
var _ Operation = (*DropColumnOp)(nil)
func (op *DropColumnOp) GetReverse() Operation {
return &AddColumnOp{
TableName: op.TableName,
ColumnName: op.ColumnName,
Column: op.Column,
}
}
func (op *DropColumnOp) DependsOn(another Operation) bool {
switch drop := another.(type) {
case *DropForeignKeyOp:
return drop.ForeignKey.DependsOnColumn(op.TableName, op.ColumnName)
case *DropPrimaryKeyOp:
return op.TableName == drop.TableName && drop.PrimaryKey.Columns.Contains(op.ColumnName)
case *ChangePrimaryKeyOp:
return op.TableName == drop.TableName && drop.Old.Columns.Contains(op.ColumnName)
}
return false
}
// AddForeignKey adds a new FOREIGN KEY constraint.
type AddForeignKeyOp struct {
ForeignKey sqlschema.ForeignKey
ConstraintName string
}
var _ Operation = (*AddForeignKeyOp)(nil)
func (op *AddForeignKeyOp) TableName() string {
return op.ForeignKey.From.TableName
}
func (op *AddForeignKeyOp) DependsOn(another Operation) bool {
switch another := another.(type) {
case *RenameTableOp:
return op.ForeignKey.DependsOnTable(another.TableName) || op.ForeignKey.DependsOnTable(another.NewName)
case *CreateTableOp:
return op.ForeignKey.DependsOnTable(another.TableName)
}
return false
}
func (op *AddForeignKeyOp) GetReverse() Operation {
return &DropForeignKeyOp{
ForeignKey: op.ForeignKey,
ConstraintName: op.ConstraintName,
}
}
// DropForeignKeyOp drops a FOREIGN KEY constraint.
type DropForeignKeyOp struct {
ForeignKey sqlschema.ForeignKey
ConstraintName string
}
var _ Operation = (*DropForeignKeyOp)(nil)
func (op *DropForeignKeyOp) TableName() string {
return op.ForeignKey.From.TableName
}
func (op *DropForeignKeyOp) GetReverse() Operation {
return &AddForeignKeyOp{
ForeignKey: op.ForeignKey,
ConstraintName: op.ConstraintName,
}
}
// AddUniqueConstraintOp adds new UNIQUE constraint to the table.
type AddUniqueConstraintOp struct {
TableName string
Unique sqlschema.Unique
}
var _ Operation = (*AddUniqueConstraintOp)(nil)
func (op *AddUniqueConstraintOp) GetReverse() Operation {
return &DropUniqueConstraintOp{
TableName: op.TableName,
Unique: op.Unique,
}
}
func (op *AddUniqueConstraintOp) DependsOn(another Operation) bool {
switch another := another.(type) {
case *AddColumnOp:
return op.TableName == another.TableName && op.Unique.Columns.Contains(another.ColumnName)
case *RenameTableOp:
return op.TableName == another.NewName
case *DropUniqueConstraintOp:
// We want to drop the constraint with the same name before adding this one.
return op.TableName == another.TableName && op.Unique.Name == another.Unique.Name
default:
return false
}
}
// DropUniqueConstraintOp drops a UNIQUE constraint.
type DropUniqueConstraintOp struct {
TableName string
Unique sqlschema.Unique
}
var _ Operation = (*DropUniqueConstraintOp)(nil)
func (op *DropUniqueConstraintOp) DependsOn(another Operation) bool {
if rename, ok := another.(*RenameTableOp); ok {
return op.TableName == rename.NewName
}
return false
}
func (op *DropUniqueConstraintOp) GetReverse() Operation {
return &AddUniqueConstraintOp{
TableName: op.TableName,
Unique: op.Unique,
}
}
// ChangeColumnTypeOp set a new data type for the column.
// The two types should be such that the data can be auto-casted from one to another.
// E.g. reducing VARCHAR lenght is not possible in most dialects.
// AutoMigrator does not enforce or validate these rules.
type ChangeColumnTypeOp struct {
TableName string
Column string
From sqlschema.Column
To sqlschema.Column
}
var _ Operation = (*ChangeColumnTypeOp)(nil)
func (op *ChangeColumnTypeOp) GetReverse() Operation {
return &ChangeColumnTypeOp{
TableName: op.TableName,
Column: op.Column,
From: op.To,
To: op.From,
}
}
// DropPrimaryKeyOp drops the table's PRIMARY KEY.
type DropPrimaryKeyOp struct {
TableName string
PrimaryKey sqlschema.PrimaryKey
}
var _ Operation = (*DropPrimaryKeyOp)(nil)
func (op *DropPrimaryKeyOp) GetReverse() Operation {
return &AddPrimaryKeyOp{
TableName: op.TableName,
PrimaryKey: op.PrimaryKey,
}
}
// AddPrimaryKeyOp adds a new PRIMARY KEY to the table.
type AddPrimaryKeyOp struct {
TableName string
PrimaryKey sqlschema.PrimaryKey
}
var _ Operation = (*AddPrimaryKeyOp)(nil)
func (op *AddPrimaryKeyOp) GetReverse() Operation {
return &DropPrimaryKeyOp{
TableName: op.TableName,
PrimaryKey: op.PrimaryKey,
}
}
func (op *AddPrimaryKeyOp) DependsOn(another Operation) bool {
switch another := another.(type) {
case *AddColumnOp:
return op.TableName == another.TableName && op.PrimaryKey.Columns.Contains(another.ColumnName)
}
return false
}
// ChangePrimaryKeyOp changes the PRIMARY KEY of the table.
type ChangePrimaryKeyOp struct {
TableName string
Old sqlschema.PrimaryKey
New sqlschema.PrimaryKey
}
var _ Operation = (*AddPrimaryKeyOp)(nil)
func (op *ChangePrimaryKeyOp) GetReverse() Operation {
return &ChangePrimaryKeyOp{
TableName: op.TableName,
Old: op.New,
New: op.Old,
}
}
// comment denotes an Operation that cannot be executed.
//
// Operations, which cannot be reversed due to current technical limitations,
// may return &comment with a helpful message from their GetReverse() method.
//
// Chnagelog should skip it when applying operations or output as log message,
// and write it as an SQL comment when creating migration files.
type comment string
var _ Operation = (*comment)(nil)
func (c *comment) GetReverse() Operation { return c }