mirror of
https://codeberg.org/superseriousbusiness/gotosocial.git
synced 2024-12-23 17:40:20 +03:00
340 lines
9.3 KiB
Go
340 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 }
|