Repository: ariga/atlas
Pull request #1847 (2023-07-11 13:32:07)

sql/mysql: implement unmarshal spec for views


Patch:
diff --git a/sql/mysql/sqlspec.go b/sql/mysql/sqlspec.go
index b4843862..7ac13390 100644
--- a/sql/mysql/sqlspec.go
+++ b/sql/mysql/sqlspec.go
@@ -22,6 +22,7 @@ import (
 
 type doc struct {
 	Tables  []*sqlspec.Table  `spec:"table"`
+	Views   []*sqlspec.View   `spec:"view"`
 	Schemas []*sqlspec.Schema `spec:"schema"`
 }
 
@@ -34,8 +35,8 @@ func evalSpec(p *hclparse.Parser, v any, input map[string]cty.Value) error {
 			return err
 		}
 		if err := specutil.Scan(v,
-			&specutil.ScanDoc{Schemas: d.Schemas, Tables: d.Tables},
-			&specutil.ScanFuncs{Table: convertTable},
+			&specutil.ScanDoc{Schemas: d.Schemas, Tables: d.Tables, Views: d.Views},
+			&specutil.ScanFuncs{Table: convertTable, View: convertView},
 		); err != nil {
 			return fmt.Errorf("mysql: failed converting to *schema.Realm: %w", err)
 		}
@@ -58,15 +59,14 @@ func evalSpec(p *hclparse.Parser, v any, input map[string]cty.Value) error {
 		}
 		r := &schema.Realm{}
 		if err := specutil.Scan(r,
-			&specutil.ScanDoc{Schemas: d.Schemas, Tables: d.Tables},
-			&specutil.ScanFuncs{Table: convertTable},
+			&specutil.ScanDoc{Schemas: d.Schemas, Tables: d.Tables, Views: d.Views},
+			&specutil.ScanFuncs{Table: convertTable, View: convertView},
 		); err != nil {
 			return err
 		}
 		if err := convertCharset(d.Schemas[0], &r.Schemas[0].Attrs); err != nil {
 			return err
 		}
-		r.Schemas[0].Realm = nil
 		*v = *r.Schemas[0]
 	case schema.Schema, schema.Realm:
 		return fmt.Errorf("mysql: Eval expects a pointer: received %[1]T, expected *%[1]T", v)
@@ -84,6 +84,7 @@ func MarshalSpec(v any, marshaler schemahcl.Marshaler) ([]byte, error) {
 var (
 	hclState = schemahcl.New(
 		schemahcl.WithTypes("table.column.type", TypeRegistry.Specs()),
+		schemahcl.WithTypes("view.column.type", TypeRegistry.Specs()),
 		schemahcl.WithScopedEnums("table.engine", EngineInnoDB, EngineMyISAM, EngineMemory, EngineCSV, EngineNDB),
 		schemahcl.WithScopedEnums("table.index.type", IndexTypeBTree, IndexTypeHash, IndexTypeFullText, IndexTypeSpatial),
 		schemahcl.WithScopedEnums("table.primary_key.type", IndexTypeBTree, IndexTypeHash, IndexTypeFullText, IndexTypeSpatial),
@@ -133,6 +134,17 @@ func convertTable(spec *sqlspec.Table, parent *schema.Schema) (*schema.Table, er
 	return t, err
 }
 
+// convertView converts a sqlspec.View to a schema.View.
+func convertView(spec *sqlspec.View, parent *schema.Schema) (*schema.View, error) {
+	v, err := specutil.View(spec, parent, func(c *sqlspec.Column, _ *schema.View) (*schema.Column, error) {
+		return specutil.Column(c, convertColumnType)
+	})
+	if err != nil {
+		return nil, err
+	}
+	return v, nil
+}
+
 // convertPK converts a sqlspec.PrimaryKey into a schema.Index.
 func convertPK(spec *sqlspec.PrimaryKey, parent *schema.Table) (*schema.Index, error) {
 	idx, err := specutil.PrimaryKey(spec, parent)


Test patch:
diff --git a/sql/mysql/sqlspec_test.go b/sql/mysql/sqlspec_test.go
index 3be38e67..7ae3011c 100644
--- a/sql/mysql/sqlspec_test.go
+++ b/sql/mysql/sqlspec_test.go
@@ -104,9 +104,7 @@ table "accounts" {
 	err := EvalHCLBytes([]byte(f), &s, nil)
 	require.NoError(t, err)
 
-	exp := &schema.Schema{
-		Name: "schema",
-	}
+	exp := schema.NewRealm(schema.New("schema")).Schemas[0]
 	exp.Tables = []*schema.Table{
 		{
 			Name:   "table",
@@ -266,6 +264,63 @@ table "accounts" {
 	require.EqualValues(t, exp, &s)
 }
 
+func TestUnmarshalViews(t *testing.T) {
+	f := `table "t1" {
+  schema = schema.public
+  column "id" {
+    null = false
+    type = int
+  }
+}
+view "v1" {
+ schema = schema.public
+ as     = "SELECT * FROM t2 WHERE id IS NOT NULL"
+}
+view "v2" {
+  schema = schema.public
+  column "id" {
+    null = false
+    type = int
+  }
+  as      = "SELECT * FROM t3 WHERE id IS NOT NULL ORDER BY id"
+  comment = "view comment"
+}
+view "v3" {
+  schema     = schema.public
+  as         = "SELECT * FROM v2 JOIN t1 USING (id)"
+  depends_on = [view.v1, table.t1]
+}
+schema "public" {
+}
+`
+	var (
+		r   schema.Realm
+		s   schema.Schema
+		exp = schema.New("public").
+			AddTables(
+				schema.NewTable("t1").
+					AddColumns(
+						schema.NewIntColumn("id", "int"),
+					),
+			).
+			AddViews(
+				schema.NewView("v1", "SELECT * FROM t2 WHERE id IS NOT NULL"),
+				schema.NewView("v2", "SELECT * FROM t3 WHERE id IS NOT NULL ORDER BY id").
+					AddColumns(
+						schema.NewIntColumn("id", "int"),
+					).
+					SetComment("view comment"),
+			)
+	)
+	exp.AddViews(
+		schema.NewView("v3", "SELECT * FROM v2 JOIN t1 USING (id)").
+			AddDeps(exp.Views[0], exp.Tables[0]),
+	)
+	r.AddSchemas(exp)
+	require.NoError(t, EvalHCLBytes([]byte(f), &s, nil))
+	require.EqualValues(t, exp, &s)
+}
+
 func TestMarshalSpec_Charset(t *testing.T) {
 	s := &schema.Schema{
 		Name: "test",
@@ -688,6 +743,7 @@ table "users" {
 				),
 		)
 	exp.Tables[0].Columns[0].Indexes = nil
+	schema.NewRealm(exp)
 	require.EqualValues(t, exp, &s)
 }
 
@@ -755,6 +811,7 @@ schema "test" {
 		Parts: []*schema.IndexPart{{C: exp.Tables[0].Columns[0]}},
 		Attrs: []schema.Attr{&IndexType{T: IndexTypeHash}},
 	})
+	schema.NewRealm(exp)
 	require.EqualValues(t, exp, &s)
 }
 
@@ -1001,6 +1058,7 @@ table "users" {
 					schema.NewIntColumn("c4", "int").SetGeneratedExpr(&schema.GeneratedExpr{Expr: "c3 * 2", Type: "STORED"}),
 				),
 		)
+	schema.NewRealm(exp)
 	require.EqualValues(t, exp, &s)
 }