Repository: ariga/atlas
Pull request #3053 (2024-08-18 07:01:54)

sql/internal/sqlx: minor improvements to changes ordering


Patch:
diff --git a/sql/internal/sqlx/plan.go b/sql/internal/sqlx/plan.go
index ae35f31c..3fc73a6f 100644
--- a/sql/internal/sqlx/plan.go
+++ b/sql/internal/sqlx/plan.go
@@ -594,6 +594,9 @@ func dependsOn(c1, c2 schema.Change, opts SortOptions) bool {
 		}
 		return depOfAdd(c1.To.Deps, c2)
 	case *schema.AddFunc:
+		if depOfAdd(c1.F.Deps, c2) {
+			return true
+		}
 		switch c2 := c2.(type) {
 		case *schema.AddSchema:
 			return c1.F.Schema.Name == c2.S.Name
@@ -623,7 +626,9 @@ func dependsOn(c1, c2 schema.Change, opts SortOptions) bool {
 				return true
 			}
 		}
-		return depOfAdd(c1.F.Deps, c2)
+		// If object is not defined explicitly in the depends_on list,
+		// and not detected by the cases above, it is not a dependency.
+		return false
 	case *schema.DropFunc:
 		switch c2 := c2.(type) {
 		case *schema.DropFunc:
@@ -826,11 +831,20 @@ func depOfAdd(refs []schema.Object, c schema.Change) bool {
 	var o schema.Object
 	switch c := c.(type) {
 	case *schema.AddTable:
-		o = c.T
+		return slices.ContainsFunc(refs, func(o schema.Object) bool {
+			t, ok := o.(*schema.Table)
+			return ok && SameTable(c.T, t)
+		})
 	case *schema.ModifyTable:
-		o = c.T
+		return slices.ContainsFunc(refs, func(o schema.Object) bool {
+			t, ok := o.(*schema.Table)
+			return ok && SameTable(c.T, t)
+		})
 	case *schema.AddView:
-		o = c.V
+		return slices.ContainsFunc(refs, func(o schema.Object) bool {
+			v, ok := o.(*schema.View)
+			return ok && SameView(c.V, v)
+		})
 	case *schema.AddObject:
 		o = c.O
 	case *schema.AddTrigger:
@@ -867,6 +881,14 @@ func dependsOnT(t1, t2 schema.Type) bool {
 	return t1 == t2 || schema.UnderlyingType(t1) == t2
 }
 
+// SameView reports if the two objects represent the same view.
+func SameView(v1, v2 *schema.View) bool {
+	if v1 == nil || v2 == nil {
+		return v1 == v2
+	}
+	return v1.Name == v2.Name && SameSchema(v1.Schema, v2.Schema)
+}
+
 // SameTable reports if the two objects represent the same table.
 func SameTable(t1, t2 *schema.Table) bool {
 	if t1 == nil || t2 == nil {


Test patch:
diff --git a/sql/internal/sqlx/plan_test.go b/sql/internal/sqlx/plan_test.go
index 274b6a45..fbd547b8 100644
--- a/sql/internal/sqlx/plan_test.go
+++ b/sql/internal/sqlx/plan_test.go
@@ -237,6 +237,39 @@ func TestSortChanges(t *testing.T) {
 		&schema.DropFunc{F: f2},
 	}, nil)
 	require.Equal(t, []schema.Change{&schema.DropFunc{F: f2}, &schema.AddFunc{F: f1}}, planned)
+
+	// Do not change the order in case there is no
+	// dependency between the function and the view.
+	v1 := schema.NewView("v1", "select 1")
+	planned = SortChanges([]schema.Change{
+		&schema.AddFunc{F: f1},
+		&schema.AddView{V: v1},
+	}, nil)
+	require.IsType(t, &schema.AddFunc{}, planned[0])
+	require.IsType(t, &schema.AddView{}, planned[1])
+	// Respect dependencies between views and functions.
+	f1.Deps = []schema.Object{v1}
+	planned = SortChanges([]schema.Change{
+		&schema.AddFunc{F: f1},
+		&schema.AddView{V: v1},
+	}, nil)
+	require.IsType(t, &schema.AddView{}, planned[0])
+	require.IsType(t, &schema.AddFunc{}, planned[1])
+	// Respect copies.
+	planned = SortChanges([]schema.Change{
+		&schema.AddFunc{F: f1},
+		&schema.AddView{V: P(V(v1))},
+	}, nil)
+	require.IsType(t, &schema.AddView{}, planned[0])
+	require.IsType(t, &schema.AddFunc{}, planned[1])
+	// Respect driver-specific logic.
+	f1.Deps = nil
+	planned = SortChanges([]schema.Change{
+		&schema.AddFunc{F: f1},
+		&schema.AddView{V: v1},
+	}, &SortOptions{FuncDepV: func(*schema.Func, *schema.View) bool { return true }})
+	require.IsType(t, &schema.AddView{}, planned[0])
+	require.IsType(t, &schema.AddFunc{}, planned[1])
 }
 
 func TestSameTable(t *testing.T) {
@@ -251,3 +284,16 @@ func TestSameTable(t *testing.T) {
 	require.False(t, SameTable(t1, schema.NewTable("t1")))
 	require.False(t, SameTable(t1, schema.NewTable("t1").SetSchema(schema.New("private"))))
 }
+
+func TestSameView(t *testing.T) {
+	v1 := schema.NewView("v1", "")
+	require.True(t, SameView(v1, v1))
+	require.True(t, SameView(v1, schema.NewView("v1", "")), "same copy")
+	require.False(t, SameView(v1, schema.NewView("v2", "")))
+
+	v1.SetSchema(schema.New("public"))
+	require.True(t, SameView(v1, v1))
+	require.True(t, SameView(v1, schema.NewView("v1", "").SetSchema(schema.New("public"))), "same copy")
+	require.False(t, SameView(v1, schema.NewView("v1", "")))
+	require.False(t, SameView(v1, schema.NewView("v1", "").SetSchema(schema.New("private"))))
+}