Repository: ariga/atlas
Pull request #1848 (2023-07-11 14:00:04)

sql/sqlite: implement unmarshal spec for views


Patch:
diff --git a/sql/sqlite/sqlspec.go b/sql/sqlite/sqlspec.go
index d039ddb8..c233d73c 100644
--- a/sql/sqlite/sqlspec.go
+++ b/sql/sqlite/sqlspec.go
@@ -19,6 +19,12 @@ import (
 	"github.com/zclconf/go-cty/cty"
 )
 
+type doc struct {
+	Tables  []*sqlspec.Table  `spec:"table"`
+	Views   []*sqlspec.View   `spec:"view"`
+	Schemas []*sqlspec.Schema `spec:"schema"`
+}
+
 // evalSpec evaluates an Atlas DDL document using an unmarshaler into v by using the input.
 func evalSpec(p *hclparse.Parser, v any, input map[string]cty.Value) error {
 	switch v := v.(type) {
@@ -28,8 +34,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("specutil: failed converting to *schema.Realm: %w", err)
 		}
@@ -43,12 +49,11 @@ 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
 		}
-		r.Schemas[0].Realm = nil
 		*v = *r.Schemas[0]
 	case schema.Schema, schema.Realm:
 		return fmt.Errorf("sqlite: Eval expects a pointer: received %[1]T, expected *%[1]T", v)
@@ -92,6 +97,17 @@ func convertTable(spec *sqlspec.Table, parent *schema.Schema) (*schema.Table, er
 	return t, nil
 }
 
+// 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
+}
+
 // convertIndex converts a sqlspec.Index into a schema.Index.
 func convertIndex(spec *sqlspec.Index, t *schema.Table) (*schema.Index, error) {
 	idx, err := specutil.Index(spec, t)
@@ -253,6 +269,7 @@ var TypeRegistry = schemahcl.NewRegistry(
 var (
 	hclState = schemahcl.New(
 		schemahcl.WithTypes("table.column.type", TypeRegistry.Specs()),
+		schemahcl.WithTypes("view.column.type", TypeRegistry.Specs()),
 		schemahcl.WithScopedEnums("table.column.as.type", stored, virtual),
 		schemahcl.WithScopedEnums("table.foreign_key.on_update", specutil.ReferenceVars...),
 		schemahcl.WithScopedEnums("table.foreign_key.on_delete", specutil.ReferenceVars...),
@@ -277,8 +294,3 @@ func storedOrVirtual(s string) string {
 	}
 	return s
 }
-
-type doc struct {
-	Tables  []*sqlspec.Table  `spec:"table"`
-	Schemas []*sqlspec.Schema `spec:"schema"`
-}


Test patch:
diff --git a/sql/sqlite/sqlspec_test.go b/sql/sqlite/sqlspec_test.go
index e9f00e06..2d365647 100644
--- a/sql/sqlite/sqlspec_test.go
+++ b/sql/sqlite/sqlspec_test.go
@@ -71,9 +71,7 @@ table "accounts" {
 	strict = true
 }
 `
-	exp := &schema.Schema{
-		Name: "schema",
-	}
+	exp := schema.NewRealm(schema.New("schema")).Schemas[0]
 	exp.Tables = []*schema.Table{
 		{
 			Name:   "table",
@@ -185,6 +183,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_AutoIncrement(t *testing.T) {
 	s := &schema.Schema{
 		Name: "test",