Repository: ariga/atlas
Pull request #1898 (2023-07-25 19:28:55)

sql: stubs and codec for view check options


Patch:
diff --git a/sql/internal/specutil/convert.go b/sql/internal/specutil/convert.go
index 37c68b9d..8fa0c1c7 100644
--- a/sql/internal/specutil/convert.go
+++ b/sql/internal/specutil/convert.go
@@ -212,6 +212,13 @@ func View(spec *sqlspec.View, parent *schema.Schema, convertColumn ConvertViewCo
 	if err := convertCommentFromSpec(spec, &v.Attrs); err != nil {
 		return nil, err
 	}
+	if c, ok := spec.Extra.Attr("check_option"); ok {
+		o, err := c.String()
+		if err != nil {
+			return nil, fmt.Errorf("specutil: expect string definition for attribute view.%s.check_option: %w", spec.Name, err)
+		}
+		v.SetCheckOption(o)
+	}
 	return v, nil
 }
 
@@ -481,6 +488,15 @@ func FromView(v *schema.View, colFn ViewColumnSpecFunc) (*sqlspec.View, error) {
 			schemahcl.StringAttr("as", as),
 		},
 	}
+	if c := (schema.ViewCheckOption{}); sqlx.Has(v.Attrs, &c) {
+		switch strings.ToUpper(c.V) {
+		case schema.ViewCheckOptionNone, "":
+		case schema.ViewCheckOptionLocal, schema.ViewCheckOptionCascaded:
+			embed.Attrs = append(embed.Attrs, VarAttr("check_option", c.V))
+		default:
+			embed.Attrs = append(embed.Attrs, schemahcl.StringAttr("check_option", c.V))
+		}
+	}
 	var (
 		deps         = make([]*schemahcl.Ref, 0, len(v.Deps))
 		nameT, nameV = make(map[string]int), make(map[string]int)
diff --git a/sql/mysql/diff.go b/sql/mysql/diff.go
index c1f88666..5748e07e 100644
--- a/sql/mysql/diff.go
+++ b/sql/mysql/diff.go
@@ -102,10 +102,6 @@ func (d *diff) TableAttrDiff(from, to *schema.Table) ([]schema.Change, error) {
 	return append(changes, checks...), nil
 }
 
-func (d *diff) ViewAttrChanged(_, _ *schema.View) bool {
-	return false // Not implemented.
-}
-
 // ColumnChange returns the schema changes (if any) for migrating one column to the other.
 func (d *diff) ColumnChange(fromT *schema.Table, from, to *schema.Column) (schema.ChangeKind, error) {
 	change := sqlx.CommentChange(from.Attrs, to.Attrs)
diff --git a/sql/mysql/driver_oss.go b/sql/mysql/driver_oss.go
index 507fa7cc..0d25a06d 100644
--- a/sql/mysql/driver_oss.go
+++ b/sql/mysql/driver_oss.go
@@ -31,3 +31,7 @@ func (*state) modifyView(*schema.ModifyView) error {
 func (*state) renameView(*schema.RenameView) {
 	// unimplemented.
 }
+
+func (d *diff) ViewAttrChanged(_, _ *schema.View) bool {
+	return false // Not implemented.
+}
diff --git a/sql/mysql/sqlspec.go b/sql/mysql/sqlspec.go
index f8dc3082..9f693626 100644
--- a/sql/mysql/sqlspec.go
+++ b/sql/mysql/sqlspec.go
@@ -85,6 +85,7 @@ var (
 	hclState = schemahcl.New(
 		schemahcl.WithTypes("table.column.type", TypeRegistry.Specs()),
 		schemahcl.WithTypes("view.column.type", TypeRegistry.Specs()),
+		schemahcl.WithScopedEnums("view.check_option", schema.ViewCheckOptionLocal, schema.ViewCheckOptionCascaded),
 		schemahcl.WithScopedEnums("table.engine", EngineInnoDB, EngineMyISAM, EngineMemory, EngineCSV, EngineNDB),
 		schemahcl.WithScopedEnums("table.index.type", IndexTypeBTree, IndexTypeHash, IndexTypeFullText, IndexTypeSpatial),
 		schemahcl.WithScopedEnums("table.index.parser", IndexParserNGram, IndexParserMeCab),
diff --git a/sql/postgres/diff.go b/sql/postgres/diff.go
index 66da0ed0..285b2a31 100644
--- a/sql/postgres/diff.go
+++ b/sql/postgres/diff.go
@@ -95,10 +95,6 @@ func (d *diff) TableAttrDiff(from, to *schema.Table) ([]schema.Change, error) {
 	})...), nil
 }
 
-func (d *diff) ViewAttrChanged(_, _ *schema.View) bool {
-	return false // Not implemented.
-}
-
 // ColumnChange returns the schema changes (if any) for migrating one column to the other.
 func (d *diff) ColumnChange(_ *schema.Table, from, to *schema.Column) (schema.ChangeKind, error) {
 	change := sqlx.CommentChange(from.Attrs, to.Attrs)
diff --git a/sql/postgres/driver_oss.go b/sql/postgres/driver_oss.go
index 2b1cfe27..089d743d 100644
--- a/sql/postgres/driver_oss.go
+++ b/sql/postgres/driver_oss.go
@@ -31,3 +31,7 @@ func (*state) modifyView(*schema.ModifyView) error {
 func (*state) renameView(*schema.RenameView) {
 	// unimplemented.
 }
+
+func (d *diff) ViewAttrChanged(_, _ *schema.View) bool {
+	return false // unimplemented.
+}
diff --git a/sql/postgres/sqlspec.go b/sql/postgres/sqlspec.go
index 551d52a6..eecc1ca6 100644
--- a/sql/postgres/sqlspec.go
+++ b/sql/postgres/sqlspec.go
@@ -129,8 +129,9 @@ func MarshalSpec(v any, marshaler schemahcl.Marshaler) ([]byte, error) {
 
 var (
 	hclState = schemahcl.New(
-		schemahcl.WithTypes("view.column.type", TypeRegistry.Specs()),
 		schemahcl.WithTypes("table.column.type", TypeRegistry.Specs()),
+		schemahcl.WithTypes("view.column.type", TypeRegistry.Specs()),
+		schemahcl.WithScopedEnums("view.check_option", schema.ViewCheckOptionLocal, schema.ViewCheckOptionCascaded),
 		schemahcl.WithScopedEnums("table.index.type", IndexTypeBTree, IndexTypeBRIN, IndexTypeHash, IndexTypeGIN, IndexTypeGiST, "GiST", IndexTypeSPGiST, "SPGiST"),
 		schemahcl.WithScopedEnums("table.partition.type", PartitionTypeRange, PartitionTypeList, PartitionTypeHash),
 		schemahcl.WithScopedEnums("table.column.identity.generated", GeneratedTypeAlways, GeneratedTypeByDefault),
diff --git a/sql/schema/dsl.go b/sql/schema/dsl.go
index fb6d7f5d..5178f900 100644
--- a/sql/schema/dsl.go
+++ b/sql/schema/dsl.go
@@ -262,6 +262,12 @@ func (v *View) AddDeps(objs ...Object) *View {
 	return v
 }
 
+// SetCheckOption sets the check option of the view.
+func (v *View) SetCheckOption(opt string) *View {
+	ReplaceOrAppend(&v.Attrs, &ViewCheckOption{V: opt})
+	return v
+}
+
 // NewColumn creates a new column with the given name.
 func NewColumn(name string) *Column {
 	return &Column{Name: name}
diff --git a/sql/schema/schema.go b/sql/schema/schema.go
index 69dee6fe..704bd612 100644
--- a/sql/schema/schema.go
+++ b/sql/schema/schema.go
@@ -368,6 +368,13 @@ type (
 	}
 )
 
+// A list of known view check options.
+const (
+	ViewCheckOptionNone     = "NONE"
+	ViewCheckOptionLocal    = "LOCAL"
+	ViewCheckOptionCascaded = "CASCADED"
+)
+
 // objects.
 func (*Table) obj()    {}
 func (*View) obj()     {}


Test patch:
diff --git a/sql/postgres/sqlspec_test.go b/sql/postgres/sqlspec_test.go
index 70b26285..d3a23880 100644
--- a/sql/postgres/sqlspec_test.go
+++ b/sql/postgres/sqlspec_test.go
@@ -313,7 +313,8 @@ func TestMarshalViews(t *testing.T) {
 				),
 		).
 		AddViews(
-			schema.NewView("v1", "SELECT 1"),
+			schema.NewView("v1", "SELECT 1").
+				SetCheckOption(schema.ViewCheckOptionLocal),
 			schema.NewView("v2", "SELECT * FROM t2\n\tWHERE id IS NOT NULL"),
 			schema.NewView("v3", "SELECT * FROM t3\n\tWHERE id IS NOT NULL\n\tORDER BY id").
 				AddColumns(
@@ -338,8 +339,9 @@ func TestMarshalViews(t *testing.T) {
   }
 }
 view "v1" {
-  schema = schema.public
-  as     = "SELECT 1"
+  schema       = schema.public
+  as           = "SELECT 1"
+  check_option = LOCAL
 }
 view "v2" {
   schema = schema.public
@@ -394,9 +396,10 @@ view "v2" {
  comment = "view comment"
 }
 view "v3" {
- schema     = schema.public
- as         = "SELECT * FROM v2 JOIN t1 USING (id)"
- depends_on = [view.v1, table.t1]
+ schema       = schema.public
+ as           = "SELECT * FROM v2 JOIN t1 USING (id)"
+ check_option = LOCAL
+ depends_on   = [view.v1, table.t1]
 }
 
 table "public" "t2" {
@@ -460,6 +463,7 @@ schema "other" {}
 	)
 	public.AddViews(
 		schema.NewView("v3", "SELECT * FROM v2 JOIN t1 USING (id)").
+			SetCheckOption(schema.ViewCheckOptionLocal).
 			AddDeps(public.Views[0], public.Tables[0]),
 		schema.NewView("v4", "SELECT * FROM public.t2").
 			AddDeps(public.Tables[1]),