Skip to content

Commit

Permalink
Merge pull request #173 from erizocosmico/fix/no-circular-deps
Browse files Browse the repository at this point in the history
allow circular dependencies in the schema migration to some extent
  • Loading branch information
Miguel Molina committed Jun 13, 2017
2 parents ffb0288 + edfdbb0 commit cb22fc2
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 30 deletions.
38 changes: 30 additions & 8 deletions generator/migration.go
Expand Up @@ -89,13 +89,18 @@ type TableSchema struct {
Columns []*ColumnSchema
}

func (s *TableSchema) relationships() []string {
type relationship struct {
name string
inverse bool
}

func (s *TableSchema) relationships() []relationship {
var rels = make(map[string]struct{})
var result []string
var result []relationship
for _, c := range s.Columns {
if c.Reference != nil {
if _, ok := rels[c.Reference.Table]; !ok {
result = append(result, c.Reference.Table)
result = append(result, relationship{c.Reference.Table, c.Reference.inverse})
rels[c.Reference.Table] = struct{}{}
}
}
Expand Down Expand Up @@ -228,7 +233,8 @@ type Reference struct {
// Table is the referenced table.
Table string
// Column is the referenced column.
Column string
Column string
inverse bool
}

func (r *Reference) Equals(r2 *Reference) bool {
Expand Down Expand Up @@ -275,7 +281,15 @@ func (cs ChangeSet) sorted(dropIndex, createIndex map[string]*TableSchema) (Chan
createTables[c.Name] = c
if rels := createIndex[c.Name].relationships(); len(rels) > 0 {
for _, r := range rels {
createGraph.dependsOn(r, c.Name)
if r.name == c.Name {
continue
}

if r.inverse {
createGraph.dependsOn(c.Name, r.name)
} else {
createGraph.dependsOn(r.name, c.Name)
}
}
} else {
createGraph.add(c.Name)
Expand All @@ -284,7 +298,15 @@ func (cs ChangeSet) sorted(dropIndex, createIndex map[string]*TableSchema) (Chan
dropTables[c.Name] = c
if rels := dropIndex[c.Name].relationships(); len(rels) > 0 {
for _, r := range rels {
dropGraph.dependsOn(r, c.Name)
if r.name == c.Name {
continue
}

if r.inverse {
dropGraph.dependsOn(c.Name, r.name)
} else {
dropGraph.dependsOn(r.name, c.Name)
}
}
} else {
dropGraph.add(c.Name)
Expand Down Expand Up @@ -870,9 +892,9 @@ func (t *packageTransformer) transformRef(f *Field) (*Reference, error) {
return nil, fmt.Errorf("kallax: unable to find table for type %s in field %s of model %s. Is the model type part of the generation input?", typ, f.Name, f.Model.Name)
}

return &Reference{Table: table, Column: t.pkIndex[table].ColumnName()}, nil
return &Reference{Table: table, Column: t.pkIndex[table].ColumnName(), inverse: true}, nil
} else if f.Kind == Relationship {
return &Reference{Table: f.Model.Table, Column: f.Model.ID.ColumnName()}, nil
return &Reference{Table: f.Model.Table, Column: f.Model.ID.ColumnName(), inverse: false}, nil
}

return nil, nil
Expand Down
67 changes: 45 additions & 22 deletions generator/migration_test.go
Expand Up @@ -21,6 +21,19 @@ func TestNewMigration(t *testing.T) {
require.Equal(t, migration.Lock, new)
}

func TestNewMigration_SelfRef(t *testing.T) {
old := mkSchema()
new := mkSchema(mkTable(
"selfref",
mkCol("id", SerialColumn, true, false, nil),
mkCol("parent_id", BigIntColumn, false, false, mkRef("selfref", "id", true)),
mkCol("child_id", BigIntColumn, false, false, mkRef("selfref", "id", false)),
))

_, err := NewMigration(old, new)
require.NoError(t, err)
}

var table1 = mkTable(
"table",
mkCol("id", SerialColumn, true, true, nil),
Expand All @@ -29,7 +42,7 @@ var table1 = mkTable(

var table2 = mkTable(
"table2",
mkCol("table_id", SerialColumn, false, true, mkRef("table", "id")),
mkCol("table_id", SerialColumn, false, true, mkRef("table", "id", false)),
mkCol("num", NumericColumn(20), false, false, nil),
)

Expand Down Expand Up @@ -198,31 +211,31 @@ func TestColumnSchemaDiff(t *testing.T) {
{
"ref added",
mkCol("foo", TextColumn, false, false, nil),
mkCol("foo", TextColumn, false, false, mkRef("foo", "bar")),
mkCol("foo", TextColumn, false, false, mkRef("foo", "bar", false)),
true,
},
{
"ref removed",
mkCol("foo", TextColumn, false, false, mkRef("foo", "bar")),
mkCol("foo", TextColumn, false, false, mkRef("foo", "bar", false)),
mkCol("foo", TextColumn, false, false, nil),
true,
},
{
"ref table changed",
mkCol("foo", TextColumn, false, false, mkRef("foo", "bar")),
mkCol("foo", TextColumn, false, false, mkRef("bar", "bar")),
mkCol("foo", TextColumn, false, false, mkRef("foo", "bar", false)),
mkCol("foo", TextColumn, false, false, mkRef("bar", "bar", false)),
true,
},
{
"ref col changed",
mkCol("foo", TextColumn, false, false, mkRef("foo", "bar")),
mkCol("foo", TextColumn, false, false, mkRef("foo", "foo")),
mkCol("foo", TextColumn, false, false, mkRef("foo", "bar", false)),
mkCol("foo", TextColumn, false, false, mkRef("foo", "foo", false)),
true,
},
{
"ref col unchanged",
mkCol("foo", TextColumn, false, false, mkRef("foo", "bar")),
mkCol("foo", TextColumn, false, false, mkRef("foo", "bar")),
mkCol("foo", TextColumn, false, false, mkRef("foo", "bar", false)),
mkCol("foo", TextColumn, false, false, mkRef("foo", "bar", false)),
false,
},
{
Expand Down Expand Up @@ -368,25 +381,25 @@ func TestColumnSchemaEquals(t *testing.T) {
{
"one of the references is nil",
mkCol("foo", TextColumn, false, false, nil),
mkCol("foo", TextColumn, false, false, mkRef("a", "b")),
mkCol("foo", TextColumn, false, false, mkRef("a", "b", false)),
false,
},
{
"reference table does not match",
mkCol("foo", TextColumn, false, false, mkRef("a", "b")),
mkCol("foo", TextColumn, false, false, mkRef("b", "b")),
mkCol("foo", TextColumn, false, false, mkRef("a", "b", false)),
mkCol("foo", TextColumn, false, false, mkRef("b", "b", false)),
false,
},
{
"reference column does not match",
mkCol("foo", TextColumn, false, false, mkRef("a", "b")),
mkCol("foo", TextColumn, false, false, mkRef("a", "a")),
mkCol("foo", TextColumn, false, false, mkRef("a", "b", false)),
mkCol("foo", TextColumn, false, false, mkRef("a", "a", false)),
false,
},
{
"equal with reference",
mkCol("foo", TextColumn, false, false, mkRef("a", "b")),
mkCol("foo", TextColumn, false, false, mkRef("a", "b")),
mkCol("foo", TextColumn, false, false, mkRef("a", "b", false)),
mkCol("foo", TextColumn, false, false, mkRef("a", "b", false)),
true,
},
{
Expand All @@ -407,7 +420,7 @@ func TestChangeSetSorted(t *testing.T) {
mkTable("table2"),
mkTable(
"table1",
mkCol("foo", SerialColumn, false, false, mkRef("table2", "bar")),
mkCol("foo", SerialColumn, false, false, mkRef("table2", "bar", false)),
),
mkTable("table3"),
)
Expand All @@ -416,7 +429,7 @@ func TestChangeSetSorted(t *testing.T) {
mkTable("table4"),
mkTable(
"table5",
mkCol("foo", SerialColumn, false, false, mkRef("table4", "bar")),
mkCol("foo", SerialColumn, false, false, mkRef("table4", "bar", false)),
),
)
cs := SchemaDiff(old, new)
Expand Down Expand Up @@ -486,6 +499,16 @@ func (s *PackageTransformerSuite) SetupTest() {
s.Require().NoError(err)
}

func (s *PackageTransformerSuite) TestMigrationCircularDep() {
require := s.Require()
schema, err := s.t.transform(s.pkg)
require.NoError(err)
require.NotNil(schema)

_, err = NewMigration(mkSchema(), schema)
require.NoError(err)
}

func (s *PackageTransformerSuite) TestTransform() {
require := s.Require()
schema, err := s.t.transform(s.pkg)
Expand All @@ -498,14 +521,14 @@ func (s *PackageTransformerSuite) TestTransform() {
mkCol("id", SerialColumn, true, false, nil),
mkCol("color", ColumnType("char(6)"), false, false, nil),
mkCol("background", TextColumn, false, false, nil),
mkCol("user_id", UUIDColumn, false, false, mkRef("users", "id")),
mkCol("user_id", UUIDColumn, false, false, mkRef("users", "id", true)),
mkCol("spouse", UUIDColumn, false, false, nil),
),
mkTable(
"metadata",
mkCol("id", SerialColumn, true, false, nil),
mkCol("metadata", JSONBColumn, false, false, nil),
mkCol("profile_id", BigIntColumn, false, false, mkRef("profiles", "id")),
mkCol("profile_id", BigIntColumn, false, false, mkRef("profiles", "id", false)),
),
mkTable(
"users",
Expand Down Expand Up @@ -589,6 +612,6 @@ func mkCol(name string, typ ColumnType, pk, notNull bool, ref *Reference) *Colum
return &ColumnSchema{name, typ, pk, ref, notNull}
}

func mkRef(table, col string) *Reference {
return &Reference{table, col}
func mkRef(table, col string, inverse bool) *Reference {
return &Reference{table, col, inverse}
}

0 comments on commit cb22fc2

Please sign in to comment.