Skip to content

Commit

Permalink
sql/postgres: support diffing objects
Browse files Browse the repository at this point in the history
  • Loading branch information
a8m committed Jun 26, 2023
1 parent 206b6b9 commit e047b81
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 24 deletions.
10 changes: 10 additions & 0 deletions sql/internal/sqlx/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ type (
// from one state to the other. For example, changing schema collation.
SchemaAttrDiff(from, to *schema.Schema) []schema.Change

// SchemaObjectDiff returns a changeset for migrating schema objects from
// one state to the other. For example, changing schema custom types.
SchemaObjectDiff(from, to *schema.Schema) ([]schema.Change, error)

// TableAttrDiff returns a changeset for migrating table attributes from
// one state to the other. For example, dropping or adding a `CHECK` constraint.
TableAttrDiff(from, to *schema.Table) ([]schema.Change, error)
Expand Down Expand Up @@ -136,6 +140,12 @@ func (d *Diff) schemaDiff(from, to *schema.Schema, opts *schema.DiffOptions) ([]
Changes: change,
})
}
// Add, drop or modify objects.
change, err := d.SchemaObjectDiff(from, to)
if err != nil {
return nil, err
}
changes = opts.AddOrSkip(changes, change...)

// Drop or modify tables.
for _, t1 := range from.Tables {
Expand Down
6 changes: 6 additions & 0 deletions sql/mysql/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ func (d *diff) SchemaAttrDiff(from, to *schema.Schema) []schema.Change {
return changes
}

// SchemaObjectDiff returns a changeset for migrating schema objects from
// one state to the other.
func (*diff) SchemaObjectDiff(_, _ *schema.Schema) ([]schema.Change, error) {
return nil, nil
}

// TableAttrDiff returns a changeset for migrating table attributes from one state to the other.
func (d *diff) TableAttrDiff(from, to *schema.Table) ([]schema.Change, error) {
var changes []schema.Change
Expand Down
40 changes: 39 additions & 1 deletion sql/postgres/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,49 @@ var DefaultDiff schema.Differ = &sqlx.Diff{DiffDriver: &diff{}}
type diff struct{ conn }

// SchemaAttrDiff returns a changeset for migrating schema attributes from one state to the other.
func (d *diff) SchemaAttrDiff(_, _ *schema.Schema) []schema.Change {
func (*diff) SchemaAttrDiff(_, _ *schema.Schema) []schema.Change {
// No special schema attribute diffing for PostgreSQL.
return nil
}

// SchemaObjectDiff returns a changeset for migrating schema objects from
// one state to the other.
func (*diff) SchemaObjectDiff(from, to *schema.Schema) ([]schema.Change, error) {
var changes []schema.Change
// Drop or modify enums.
for _, o1 := range from.Objects {
e1, ok := o1.(*schema.EnumType)
if !ok {
return nil, fmt.Errorf("unsupported object type %T", o1)
}
o2, ok := to.Object(func(o schema.Object) bool {
e2, ok := o.(*schema.EnumType)
return ok && e1.T == e2.T
})
if !ok {
changes = append(changes, &schema.DropObject{O: o1})
continue
}
if e2 := o2.(*schema.EnumType); !sqlx.ValuesEqual(e1.Values, e2.Values) {
changes = append(changes, &schema.ModifyObject{From: e1, To: e2})
}
}
// Add new enums.
for _, o1 := range to.Objects {
e1, ok := o1.(*schema.EnumType)
if !ok {
return nil, fmt.Errorf("unsupported object type %T", o1)
}
if _, ok := from.Object(func(o schema.Object) bool {
e2, ok := o.(*schema.EnumType)
return ok && e1.T == e2.T
}); !ok {
changes = append(changes, &schema.AddObject{O: e1})
}
}
return changes, nil
}

// TableAttrDiff returns a changeset for migrating table attributes from one state to the other.
func (d *diff) TableAttrDiff(from, to *schema.Table) ([]schema.Change, error) {
var changes []schema.Change
Expand Down
27 changes: 8 additions & 19 deletions sql/postgres/diff_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -497,28 +497,17 @@ func TestDiff_SchemaDiff(t *testing.T) {
mock{m}.version("130000")
drv, err := Open(db)
require.NoError(t, err)
from := &schema.Schema{
Tables: []*schema.Table{
{Name: "users"},
{Name: "pets"},
},
}
to := &schema.Schema{
Tables: []*schema.Table{
{
Name: "users",
Columns: []*schema.Column{
{Name: "t2_id", Type: &schema.ColumnType{Raw: "int", Type: &schema.IntegerType{T: "int"}}},
},
},
{Name: "groups"},
},
}
from.Tables[0].Schema = from
from.Tables[1].Schema = from
from := schema.New("public").
AddTables(schema.NewTable("users"), schema.NewTable("pets")).
AddObjects(&schema.EnumType{T: "dropped"}, &schema.EnumType{T: "modified", Values: []string{"a"}}, &schema.EnumType{T: "unchanged"})
to := schema.New("public").AddTables(schema.NewTable("users").AddColumns(schema.NewIntColumn("t2_id", "int")), schema.NewTable("groups")).
AddObjects(&schema.EnumType{T: "modified", Values: []string{"b"}}, &schema.EnumType{T: "unchanged"}, &schema.EnumType{T: "added"})
changes, err := drv.SchemaDiff(from, to)
require.NoError(t, err)
require.EqualValues(t, []schema.Change{
&schema.DropObject{O: from.Objects[0]},
&schema.ModifyObject{From: from.Objects[1], To: to.Objects[0]},
&schema.AddObject{O: to.Objects[2]},
&schema.ModifyTable{T: to.Tables[0], Changes: []schema.Change{&schema.AddColumn{C: to.Tables[0].Columns[0]}}},
&schema.DropTable{T: from.Tables[1]},
&schema.AddTable{T: to.Tables[1]},
Expand Down
12 changes: 9 additions & 3 deletions sql/schema/migrate.go
Original file line number Diff line number Diff line change
Expand Up @@ -377,9 +377,11 @@ func (o *DiffOptions) Skipped(c Change) bool {
}

// AddOrSkip adds the given change to the list of changes if it is not skipped.
func (o *DiffOptions) AddOrSkip(changes Changes, c Change) Changes {
if !o.Skipped(c) {
return append(changes, c)
func (o *DiffOptions) AddOrSkip(changes Changes, cs ...Change) Changes {
for _, c := range cs {
if !o.Skipped(c) {
changes = append(changes, c)
}
}
return changes

Expand Down Expand Up @@ -540,6 +542,10 @@ func (*AddView) change() {}
func (*DropView) change() {}
func (*ModifyView) change() {}
func (*RenameView) change() {}
func (*AddObject) change() {}
func (*DropObject) change() {}
func (*ModifyObject) change() {}
func (*RenameObject) change() {}
func (*AddIndex) change() {}
func (*DropIndex) change() {}
func (*ModifyIndex) change() {}
Expand Down
8 changes: 7 additions & 1 deletion sql/sqlite/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,17 @@ var DefaultDiff schema.Differ = &sqlx.Diff{DiffDriver: &diff{}}
type diff struct{}

// SchemaAttrDiff returns a changeset for migrating schema attributes from one state to the other.
func (d *diff) SchemaAttrDiff(_, _ *schema.Schema) []schema.Change {
func (*diff) SchemaAttrDiff(_, _ *schema.Schema) []schema.Change {
// No special schema attribute diffing for SQLite.
return nil
}

// SchemaObjectDiff returns a changeset for migrating schema objects from
// one state to the other.
func (*diff) SchemaObjectDiff(_, _ *schema.Schema) ([]schema.Change, error) {
return nil, nil
}

// TableAttrDiff returns a changeset for migrating table attributes from one state to the other.
func (d *diff) TableAttrDiff(from, to *schema.Table) ([]schema.Change, error) {
var changes []schema.Change
Expand Down

0 comments on commit e047b81

Please sign in to comment.