Repository: ariga/atlas
Pull request #2779 (2024-05-14 18:20:40)

doc/page-view-plugin-update


Patch:
diff --git a/doc/md/atlas-schema/external.mdx b/doc/md/atlas-schema/external.mdx
index f2e28a79..40af35cc 100644
--- a/doc/md/atlas-schema/external.mdx
+++ b/doc/md/atlas-schema/external.mdx
@@ -138,7 +138,7 @@ crafting them by hand. The supported ORMs are:
 | <span className="badge badge--secondary">Go</span>                                                                    | [Ent](https://entgo.io/docs/versioned-migrations), [Beego](/guides/orms/beego) | <span className="badge badge--primary">MySQL</span> <span className="badge badge--primary">MariaDB</span> <span className="badge badge--primary">PostgreSQL</span> <span className="badge badge--primary">SQLite</span>                                                          |
 | <span className="badge badge--secondary">Java</span>                                                                  | [Hibernate](/guides/orms/hibernate)                                            | <span className="badge badge--primary">MySQL</span> <span className="badge badge--primary">MariaDB</span> <span className="badge badge--primary">PostgreSQL</span> <span className="badge badge--primary">SQLite</span>                                                          |
 | <span className="badge badge--secondary">JavaScript</span> <span className="badge badge--secondary">TypeScript</span> | [Sequelize](/guides/orms/sequelize), [TypeORM](/guides/orms/typeorm)           | <span className="badge badge--primary">MySQL</span> <span className="badge badge--primary">MariaDB</span> <span className="badge badge--primary">PostgreSQL</span> <span className="badge badge--primary">SQLite</span> <span className="badge badge--primary">SQL Server</span> |
-| <span className="badge badge--secondary">PHP</span>                                                                   | [Doctine](/guides/orms/doctrine)                                               | <span className="badge badge--primary">MySQL</span> <span className="badge badge--primary">MariaDB</span> <span className="badge badge--primary">PostgreSQL</span> <span className="badge badge--primary">SQLite</span> <span className="badge badge--primary">SQL Server</span> |
+| <span className="badge badge--secondary">PHP</span>                                                                   | [Doctrine](/guides/orms/doctrine)                                               | <span className="badge badge--primary">MySQL</span> <span className="badge badge--primary">MariaDB</span> <span className="badge badge--primary">PostgreSQL</span> <span className="badge badge--primary">SQLite</span> <span className="badge badge--primary">SQL Server</span> |
 
 Coming soon are EF, Laravel, and Prisma. If you are using an ORM that is not listed here and would like to see
 it supported, [let us know!](https://github.com/ariga/atlas/issues/new)
diff --git a/doc/website/package-lock.json b/doc/website/package-lock.json
index 2d39f286..38e30e96 100644
--- a/doc/website/package-lock.json
+++ b/doc/website/package-lock.json
@@ -8,7 +8,7 @@
       "name": "website",
       "version": "0.0.0",
       "dependencies": {
-        "@ariga/atlas-website": "0.0.37",
+        "@ariga/atlas-website": "0.0.40",
         "@docusaurus/core": "^2.0.0-rc.1",
         "@docusaurus/plugin-client-redirects": "^2.4.1",
         "@docusaurus/plugin-ideal-image": "^2.4.1",
@@ -296,9 +296,9 @@
       }
     },
     "node_modules/@ariga/atlas-website": {
-      "version": "0.0.37",
-      "resolved": "https://registry.npmjs.org/@ariga/atlas-website/-/atlas-website-0.0.37.tgz",
-      "integrity": "sha512-8Hq3m5qnq2X/hN6v5ZzBLunfts5oc6xAN8d2p6wPpco+ObxuaUO5YcKoXL7UWjV+UDd3d7eJeVf1HH8QgqyhGw==",
+      "version": "0.0.40",
+      "resolved": "https://registry.npmjs.org/@ariga/atlas-website/-/atlas-website-0.0.40.tgz",
+      "integrity": "sha512-22uLM3wH6P49WszMICoCXMm1lAiKVL+CrnddSzXeiFKqhSZVm9BkBJalRvNshSR4UkkQLxI3Cuk8LNi1/1t15w==",
       "dependencies": {
         "@amplitude/analytics-browser": "^1.9.4",
         "@types/amplitude-js": "^8.16.2",
@@ -24598,9 +24598,9 @@
       }
     },
     "@ariga/atlas-website": {
-      "version": "0.0.37",
-      "resolved": "https://registry.npmjs.org/@ariga/atlas-website/-/atlas-website-0.0.37.tgz",
-      "integrity": "sha512-8Hq3m5qnq2X/hN6v5ZzBLunfts5oc6xAN8d2p6wPpco+ObxuaUO5YcKoXL7UWjV+UDd3d7eJeVf1HH8QgqyhGw==",
+      "version": "0.0.40",
+      "resolved": "https://registry.npmjs.org/@ariga/atlas-website/-/atlas-website-0.0.40.tgz",
+      "integrity": "sha512-22uLM3wH6P49WszMICoCXMm1lAiKVL+CrnddSzXeiFKqhSZVm9BkBJalRvNshSR4UkkQLxI3Cuk8LNi1/1t15w==",
       "requires": {
         "@amplitude/analytics-browser": "^1.9.4",
         "@types/amplitude-js": "^8.16.2",
diff --git a/doc/website/package.json b/doc/website/package.json
index 1b7d669e..ed0e1f20 100644
--- a/doc/website/package.json
+++ b/doc/website/package.json
@@ -16,7 +16,7 @@
     "invalidate-cdn": "aws cloudfront create-invalidation --distribution-id E3VOJYSV9YO33D --paths \"/*\""
   },
   "dependencies": {
-    "@ariga/atlas-website": "0.0.37",
+    "@ariga/atlas-website": "0.0.40",
     "@docusaurus/core": "^2.0.0-rc.1",
     "@docusaurus/plugin-client-redirects": "^2.4.1",
     "@docusaurus/plugin-ideal-image": "^2.4.1",
diff --git a/sql/internal/sqlx/plan.go b/sql/internal/sqlx/plan.go
index 204c86af..b7ce971b 100644
--- a/sql/internal/sqlx/plan.go
+++ b/sql/internal/sqlx/plan.go
@@ -386,8 +386,13 @@ func byKeys[T any](m map[string]T) []struct {
 type SortOptions struct {
 	// FuncDepT reports if a function depends on the given table.
 	FuncDepT func(*schema.Func, *schema.Table) bool
+	// FuncDepV reports if a function depends on the given view.
+	FuncDepV func(*schema.Func, *schema.View) bool
 	// FuncDepO reports if a function depends on the given object.
 	FuncDepO func(*schema.Func, schema.Object) bool
+	// DefaultSchema defines the default schema (also known as "search_path") that
+	// is used by the database to search for objects if no qualifier is provided.
+	DefaultSchema string
 }
 
 // SortChanges is a helper function to sort to level changes based on their priority.
@@ -410,10 +415,10 @@ func SortChanges(changes []schema.Change, opts *SortOptions) []schema.Change {
 	// (see, dependsOn function) we push views and drop changes to the end, unless there is a dependency requirement.
 	changes = append(other, append(views, drop...)...)
 	edges := make(map[schema.Change][]schema.Change)
-	for _, c := range changes {
+	for _, c1 := range changes {
 		for _, c2 := range changes {
-			if c != c2 && dependsOn(c, c2, V(opts)) {
-				edges[c] = append(edges[c], c2)
+			if c1 != c2 && dependsOn(c1, c2, V(opts)) {
+				edges[c1] = append(edges[c1], c2)
 			}
 		}
 	}
@@ -499,7 +504,7 @@ func dependsOn(c1, c2 schema.Change, opts SortOptions) bool {
 				return true
 			}
 		case *schema.AddFunc:
-			return tableDepFunc(c1.T, c2.F)
+			return tableDepFunc(c1.T, c2.F, opts)
 		}
 		return depOfAdd(c1.T.Deps, c2)
 	case *schema.DropTable:
@@ -568,9 +573,15 @@ func dependsOn(c1, c2 schema.Change, opts SortOptions) bool {
 			return c1.V.Schema.Name == c2.S.Name
 		case *schema.DropView:
 			return c1.V.Name == c2.V.Name && sameSchema(c1.V.Schema, c2.V.Schema) // View recreation.
-		default:
-			return depOfAdd(c1.V.Deps, c2)
+		case *schema.AddObject:
+			t, ok := c2.O.(schema.Type)
+			if ok && slices.ContainsFunc(c1.V.Columns, func(c *schema.Column) bool {
+				return dependsOnT(c.Type.Type, t)
+			}) {
+				return true
+			}
 		}
+		return depOfAdd(c1.V.Deps, c2)
 	case *schema.DropView:
 		if c2, ok := c2.(*schema.DropTrigger); ok && c2.T.View == c1.V {
 			return true
@@ -589,17 +600,21 @@ func dependsOn(c1, c2 schema.Change, opts SortOptions) bool {
 		case *schema.DropFunc:
 			return c1.F.Name == c2.F.Name && sameSchema(c1.F.Schema, c2.F.Schema) // Func recreation.
 		case *schema.AddFunc:
-			if funcDep(c1.F, c2.F) {
+			if funcDep(c1.F, c2.F, opts) {
 				return true // Relies on other function or overload.
 			}
 		case *schema.ModifyFunc:
-			if funcDep(c1.F, c2.To) {
+			if funcDep(c1.F, c2.To, opts) {
 				return true // Relies on the new definition.
 			}
 		case *schema.AddTable:
 			if opts.FuncDepT != nil && opts.FuncDepT(c1.F, c2.T) {
 				return true
 			}
+		case *schema.AddView:
+			if opts.FuncDepV != nil && opts.FuncDepV(c1.F, c2.V) {
+				return true
+			}
 		case *schema.AddObject:
 			t, ok := c2.O.(schema.Type)
 			if ok && (c1.F.Ret == t || slices.ContainsFunc(c1.F.Args, func(f *schema.FuncArg) bool {
@@ -612,12 +627,12 @@ func dependsOn(c1, c2 schema.Change, opts SortOptions) bool {
 	case *schema.DropFunc:
 		switch c2 := c2.(type) {
 		case *schema.DropFunc:
-			if funcDep(c2.F, c1.F) {
+			if funcDep(c2.F, c1.F, opts) {
 				// If f1 depends on f2, f1 should be dropped before f2.
 				return true
 			}
 		case *schema.ModifyFunc:
-			if funcDep(c2.From, c1.F) {
+			if funcDep(c2.From, c1.F, opts) {
 				// If f1 depends on previous definition of f2, f1 should be dropped before f2.
 				return true
 			}
@@ -629,11 +644,11 @@ func dependsOn(c1, c2 schema.Change, opts SortOptions) bool {
 			if c1.From.Name == c2.F.Name && sameSchema(c1.From.Schema, c2.F.Schema) {
 				return true // Func modification relies on its creation.
 			}
-			if funcDep(c1.To, c2.F) {
+			if funcDep(c1.To, c2.F, opts) {
 				return true // New definition relies on a new function.
 			}
 		case *schema.ModifyFunc:
-			if funcDep(c1.To, c2.To) {
+			if funcDep(c1.To, c2.To, opts) {
 				return true // New definition relies on a new definition.
 			}
 		}
@@ -645,11 +660,11 @@ func dependsOn(c1, c2 schema.Change, opts SortOptions) bool {
 		case *schema.DropProc:
 			return c1.P.Name == c2.P.Name && sameSchema(c1.P.Schema, c2.P.Schema) // Proc recreation.
 		case *schema.AddProc:
-			if procDep(c1.P, c2.P) {
+			if procDep(c1.P, c2.P, opts) {
 				return true // Relies on other procedure or overload.
 			}
 		case *schema.ModifyProc:
-			if procDep(c1.P, c2.To) {
+			if procDep(c1.P, c2.To, opts) {
 				return true // Relies on the new definition.
 			}
 		case *schema.AddObject:
@@ -663,12 +678,12 @@ func dependsOn(c1, c2 schema.Change, opts SortOptions) bool {
 	case *schema.DropProc:
 		switch c2 := c2.(type) {
 		case *schema.DropProc:
-			if procDep(c2.P, c1.P) {
+			if procDep(c2.P, c1.P, opts) {
 				// If f1 depends on f2, f1 should be dropped before f2.
 				return true
 			}
 		case *schema.ModifyProc:
-			if procDep(c2.From, c1.P) {
+			if procDep(c2.From, c1.P, opts) {
 				// If f1 depends on previous definition of f2, f1 should be dropped before f2.
 				return true
 			}
@@ -680,11 +695,11 @@ func dependsOn(c1, c2 schema.Change, opts SortOptions) bool {
 			if c1.From.Name == c2.P.Name && sameSchema(c1.From.Schema, c2.P.Schema) {
 				return true // Proc modification relies on its creation.
 			}
-			if procDep(c1.To, c2.P) {
+			if procDep(c1.To, c2.P, opts) {
 				return true // New definition relies on a new procedure.
 			}
 		case *schema.ModifyProc:
-			if procDep(c1.To, c2.To) {
+			if procDep(c1.To, c2.To, opts) {
 				return true // New definition relies on a new definition.
 			}
 		}
@@ -816,14 +831,22 @@ func depOfAdd(refs []schema.Object, c schema.Change) bool {
 		o = c.T
 	case *schema.AddView:
 		o = c.V
-	case *schema.AddFunc:
-		o = c.F
-	case *schema.AddProc:
-		o = c.P
 	case *schema.AddObject:
 		o = c.O
 	case *schema.AddTrigger:
 		o = c.T
+	// Check functions and procedures by
+	// names as they might have overloads.
+	case *schema.AddFunc:
+		return slices.ContainsFunc(refs, func(o schema.Object) bool {
+			f, ok := o.(*schema.Func)
+			return ok && c.F.Name == f.Name && sameSchema(c.F.Schema, f.Schema)
+		})
+	case *schema.AddProc:
+		return slices.ContainsFunc(refs, func(o schema.Object) bool {
+			f, ok := o.(*schema.Proc)
+			return ok && c.P.Name == f.Name && sameSchema(c.P.Schema, f.Schema)
+		})
 	default:
 		return false
 	}
diff --git a/sql/internal/sqlx/sqlx_oss.go b/sql/internal/sqlx/sqlx_oss.go
index 0f83da82..834686f1 100644
--- a/sql/internal/sqlx/sqlx_oss.go
+++ b/sql/internal/sqlx/sqlx_oss.go
@@ -22,16 +22,16 @@ func (*Diff) triggerDiff(_, _ interface {
 }
 
 // funcDep returns true if f1 depends on f2.
-func funcDep(_, _ *schema.Func) bool {
+func funcDep(_, _ *schema.Func, _ SortOptions) bool {
 	return false // unimplemented.
 }
 
 // procDep returns true if p1 depends on p2.
-func procDep(_, _ *schema.Proc) bool {
+func procDep(_, _ *schema.Proc, _ SortOptions) bool {
 	return false // unimplemented.
 }
 
-func tableDepFunc(_ *schema.Table, _ *schema.Func) bool {
+func tableDepFunc(*schema.Table, *schema.Func, SortOptions) bool {
 	return false // unimplemented.
 }
 
diff --git a/sql/postgres/migrate.go b/sql/postgres/migrate.go
index e5f87de6..5fa5a5e8 100644
--- a/sql/postgres/migrate.go
+++ b/sql/postgres/migrate.go
@@ -860,7 +860,7 @@ func (s *state) columnComment(src schema.Change, t *schema.Table, c *schema.Colu
 }
 
 func (s *state) indexComment(src schema.Change, t *schema.Table, idx *schema.Index, to, from string) *migrate.Change {
-	b := s.Build("COMMENT ON INDEX").Ident(idx.Name).P("IS")
+	b := s.Build("COMMENT ON INDEX").SchemaResource(t.Schema, idx.Name).P("IS")
 	return &migrate.Change{
 		Cmd:     b.Clone().P(quote(to)).String(),
 		Source:  src,


Test patch:
diff --git a/sql/postgres/migrate_test.go b/sql/postgres/migrate_test.go
index 686dac8a..7ba9db38 100644
--- a/sql/postgres/migrate_test.go
+++ b/sql/postgres/migrate_test.go
@@ -433,6 +433,38 @@ func TestPlanChanges(t *testing.T) {
 				},
 			},
 		},
+		// Qualified DROP TABLE plan.
+		{
+			changes: []schema.Change{
+				&schema.DropTable{
+					T: func() *schema.Table {
+						id := schema.NewIntColumn("id", "int")
+						return schema.NewTable("posts").
+							SetSchema(schema.New("private")).
+							SetComment("a8m's posts").
+							AddColumns(id).
+							AddIndexes(
+								schema.NewIndex("idx").AddColumns(id).SetComment("a8m's index"),
+							)
+					}(),
+				},
+			},
+			wantPlan: &migrate.Plan{
+				Reversible:    true,
+				Transactional: true,
+				Changes: []*migrate.Change{
+					{
+						Cmd: `DROP TABLE "private"."posts"`,
+						Reverse: []string{
+							`CREATE TABLE "private"."posts" ("id" integer NOT NULL)`,
+							`CREATE INDEX "idx" ON "private"."posts" ("id")`,
+							`COMMENT ON TABLE "private"."posts" IS 'a8m''s posts'`,
+							`COMMENT ON INDEX "private"."idx" IS 'a8m''s index'`,
+						},
+					},
+				},
+			},
+		},
 		// Drop a primary key.
 		{
 			changes: []schema.Change{