nebius/SWE-rebench-V2-PRs huggingface dataset:
Repository: ariga/atlas
Pull request #1773 (2023-06-22 12:14:21)

sql/sqlite: table options diff


Patch:
diff --git a/sql/sqlite/diff.go b/sql/sqlite/diff.go
index 5d55bb37..b2b18d26 100644
--- a/sql/sqlite/diff.go
+++ b/sql/sqlite/diff.go
@@ -31,15 +31,17 @@ func (d *diff) SchemaAttrDiff(_, _ *schema.Schema) []schema.Change {
 // TableAttrDiff returns a changeset for migrating table attributes from one state to the other.
 func (d *diff) TableAttrDiff(from, to *schema.Table) ([]schema.Change, error) {
 	var changes []schema.Change
-	switch {
-	case sqlx.Has(from.Attrs, &WithoutRowID{}) && !sqlx.Has(to.Attrs, &WithoutRowID{}):
-		changes = append(changes, &schema.DropAttr{
-			A: &WithoutRowID{},
-		})
-	case !sqlx.Has(from.Attrs, &WithoutRowID{}) && sqlx.Has(to.Attrs, &WithoutRowID{}):
-		changes = append(changes, &schema.AddAttr{
-			A: &WithoutRowID{},
-		})
+	for _, a := range []schema.Attr{&WithoutRowID{}, &Strict{}} {
+		switch {
+		case sqlx.Has(from.Attrs, a) && !sqlx.Has(to.Attrs, a):
+			changes = append(changes, &schema.DropAttr{
+				A: a,
+			})
+		case !sqlx.Has(from.Attrs, a) && sqlx.Has(to.Attrs, a):
+			changes = append(changes, &schema.AddAttr{
+				A: a,
+			})
+		}
 	}
 	return append(changes, sqlx.CheckDiff(from, to)...), nil
 }


Test patch:
diff --git a/sql/sqlite/diff_test.go b/sql/sqlite/diff_test.go
index ef5b1863..70159c62 100644
--- a/sql/sqlite/diff_test.go
+++ b/sql/sqlite/diff_test.go
@@ -27,23 +27,29 @@ func TestDiff_TableDiff(t *testing.T) {
 			to:   &schema.Table{Name: "users"},
 		},
 		{
-			name: "add attr",
+			name: "add attrs",
 			from: &schema.Table{Name: "t1", Schema: &schema.Schema{Name: "public"}},
-			to:   &schema.Table{Name: "t1", Attrs: []schema.Attr{&WithoutRowID{}}},
+			to:   &schema.Table{Name: "t1", Attrs: []schema.Attr{&WithoutRowID{}, &Strict{}}},
 			wantChanges: []schema.Change{
 				&schema.AddAttr{
 					A: &WithoutRowID{},
 				},
+				&schema.AddAttr{
+					A: &Strict{},
+				},
 			},
 		},
 		{
-			name: "drop attr",
-			from: &schema.Table{Name: "t1", Attrs: []schema.Attr{&WithoutRowID{}}},
+			name: "drop attrs",
+			from: &schema.Table{Name: "t1", Attrs: []schema.Attr{&WithoutRowID{}, &Strict{}}},
 			to:   &schema.Table{Name: "t1"},
 			wantChanges: []schema.Change{
 				&schema.DropAttr{
 					A: &WithoutRowID{},
 				},
+				&schema.DropAttr{
+					A: &Strict{},
+				},
 			},
 		},
 		{
Repository: ariga/atlas
Pull request #1811 (2023-07-02 07:09:45)

schemahcl: count nested locals as edges when traversing references


Patch:
diff --git a/schemahcl/context.go b/schemahcl/context.go
index 971f04fb..5fd2b464 100644
--- a/schemahcl/context.go
+++ b/schemahcl/context.go
@@ -160,7 +160,7 @@ func (s *State) evalReferences(ctx *hcl.EvalContext, body *hclsyntax.Body) error
 		for _, e := range n.edges() {
 			var addr [3]string
 			switch root := e.RootName(); {
-			case root == RefLocal && len(e) == 2:
+			case root == RefLocal && len(e) > 1:
 				addr = [3]string{RefLocal, e[1].(hcl.TraverseAttr).Name, ""}
 			case root == RefData && len(e) > 2:
 				addr = [3]string{RefData, e[1].(hcl.TraverseAttr).Name, e[2].(hcl.TraverseAttr).Name}


Test patch:
diff --git a/schemahcl/hcl_test.go b/schemahcl/hcl_test.go
index 925db613..1e703c2e 100644
--- a/schemahcl/hcl_test.go
+++ b/schemahcl/hcl_test.go
@@ -551,6 +551,9 @@ locals {
   c = "local-c-ref-data-a: ${data.text.a.output}"
   d = "local-d"
   host = "atlasgo.io"
+  obj = {
+    k = "obj-v"
+  }
 }
 
 data "sql" "tenants" {
@@ -586,6 +589,9 @@ data "remote_dir" "migrations" {
   host = atlas.auth.host
 }
 
+data "text" "obj" {
+  value = local.obj.k
+}
 vs = [
   local.a,
   local.b,
@@ -594,6 +600,7 @@ vs = [
   data.text.a.output,
   data.text.b.output,
   data.remote_dir.migrations.url,
+  data.text.obj.output,
 ]
 `)
 	)
@@ -606,6 +613,7 @@ vs = [
 		"data-text-a-ref-data-sql-tenants: data-sql-tenants",
 		"data-text-b-ref-local-d: local-d",
 		"atlas://atlasgo.io/ent/migrations",
+		"obj-v",
 	}, doc.Values)
 
 	b = []byte(`locals { a = local.a }`)
Repository: ariga/atlas
Pull request #1814 (2023-07-02 20:01:33)

sql/sqlspec: marshal view dependencies


Patch:
diff --git a/sql/internal/specutil/convert.go b/sql/internal/specutil/convert.go
index f323f4ba..10c47f21 100644
--- a/sql/internal/specutil/convert.go
+++ b/sql/internal/specutil/convert.go
@@ -396,6 +396,22 @@ func FromView(v *schema.View, colFn ViewColumnSpecFunc) (*sqlspec.View, error) {
 			schemahcl.StringAttr("as", as),
 		},
 	}
+	deps := make([]*schemahcl.Ref, 0, len(v.Deps))
+	for _, d := range v.Deps {
+		switch d := d.(type) {
+		case *schema.Table:
+			deps = append(deps, schemahcl.BuildRef([]schemahcl.PathIndex{
+				{T: "table", V: []string{d.Name}},
+			}))
+		case *schema.View:
+			deps = append(deps, schemahcl.BuildRef([]schemahcl.PathIndex{
+				{T: "view", V: []string{d.Name}},
+			}))
+		}
+	}
+	if len(deps) > 0 {
+		embed.Attrs = append(embed.Attrs, schemahcl.RefsAttr("depends_on", deps...))
+	}
 	convertCommentFromSchema(v.Attrs, &embed.Attrs)
 	spec.Extra.Children = append(spec.Extra.Children, embed)
 	return spec, nil


Test patch:
diff --git a/sql/postgres/sqlspec_test.go b/sql/postgres/sqlspec_test.go
index fa8c9722..e254d713 100644
--- a/sql/postgres/sqlspec_test.go
+++ b/sql/postgres/sqlspec_test.go
@@ -264,6 +264,12 @@ enum "account_type" {
 
 func TestMarshalViews(t *testing.T) {
 	s := schema.New("public").
+		AddTables(
+			schema.NewTable("t1").
+				AddColumns(
+					schema.NewIntColumn("id", "int"),
+				),
+		).
 		AddViews(
 			schema.NewView("v1", "SELECT 1"),
 			schema.NewView("v2", "SELECT * FROM t2\n\tWHERE id IS NOT NULL"),
@@ -273,9 +279,23 @@ func TestMarshalViews(t *testing.T) {
 				).
 				SetComment("view comment"),
 		)
+	s.AddViews(
+		schema.NewView("v4", "SELECT * FROM v2 JOIN t1 USING (id)").
+			AddDeps(
+				s.Views[1],
+				s.Tables[0],
+			),
+	)
 	buf, err := MarshalHCL(s)
 	require.NoError(t, err)
-	f := `view "v1" {
+	f := `table "t1" {
+  schema = schema.public
+  column "id" {
+    null = false
+    type = int
+  }
+}
+view "v1" {
   schema = schema.public
   as     = "SELECT 1"
 }
@@ -299,6 +319,11 @@ view "v3" {
   SQL
   comment = "view comment"
 }
+view "v4" {
+  schema     = schema.public
+  as         = "SELECT * FROM v2 JOIN t1 USING (id)"
+  depends_on = [view.v2, table.t1]
+}
 schema "public" {
 }
 `
Repository: ariga/atlas
Pull request #1817 (2023-07-04 07:54:34)

schemahcl: fix panic in Type method
I encountered  nil pointer deference panic when using `mysql.EvalHCLBytes` func,
see  relevant stack trace part.


Patch:
diff --git a/schemahcl/types.go b/schemahcl/types.go
index 6651882f..2db4a6c3 100644
--- a/schemahcl/types.go
+++ b/schemahcl/types.go
@@ -266,6 +266,9 @@ func (r *TypeRegistry) Specs() []*TypeSpec {
 
 // Type converts a *schemahcl.Type into a schema.Type.
 func (r *TypeRegistry) Type(typ *Type, extra []*Attr) (schema.Type, error) {
+	if typ == nil {
+		return nil, errors.New("specutil: nil type")
+	}
 	typeSpec, ok := r.findT(typ.T)
 	if !ok {
 		return r.parser(typ.T)


Test patch:
diff --git a/sql/internal/spectest/spectest.go b/sql/internal/spectest/spectest.go
index 0cb27ca8..03a126c9 100644
--- a/sql/internal/spectest/spectest.go
+++ b/sql/internal/spectest/spectest.go
@@ -24,6 +24,8 @@ func RegistrySanityTest(t *testing.T, registry *schemahcl.TypeRegistry, skip []s
 			continue
 		}
 		t.Run(ts.Name, func(t *testing.T) {
+			_, err := registry.Type(nil, nil)
+			require.EqualError(t, err, "specutil: nil type")
 			spec := dummyType(t, ts)
 			styp, err := registry.Type(spec, nil)
 			require.NoError(t, err)
Repository: ariga/atlas
Pull request #1834 (2023-07-08 18:41:43)

schemahcl: add helper/debug print function


Patch:
diff --git a/schemahcl/stdlib.go b/schemahcl/stdlib.go
index a3234c3f..7a05a3a3 100644
--- a/schemahcl/stdlib.go
+++ b/schemahcl/stdlib.go
@@ -5,6 +5,7 @@
 package schemahcl
 
 import (
+	"fmt"
 	"net/url"
 	"strconv"
 
@@ -14,6 +15,7 @@ import (
 	"github.com/zclconf/go-cty/cty/convert"
 	"github.com/zclconf/go-cty/cty/function"
 	"github.com/zclconf/go-cty/cty/function/stdlib"
+	"github.com/zclconf/go-cty/cty/json"
 )
 
 func stdTypes(ctx *hcl.EvalContext) *hcl.EvalContext {
@@ -128,6 +130,7 @@ func stdFuncs() map[string]function.Function {
 		"min":             stdlib.MinFunc,
 		"parseint":        stdlib.ParseIntFunc,
 		"pow":             stdlib.PowFunc,
+		"print":           printFunc,
 		"range":           stdlib.RangeFunc,
 		"regex":           stdlib.RegexFunc,
 		"regexall":        stdlib.RegexAllFunc,
@@ -306,3 +309,23 @@ var urlEscape = function.New(&function.Spec{
 		return cty.StringVal(u), nil
 	},
 })
+
+var printFunc = function.New(&function.Spec{
+	Params: []function.Parameter{
+		{
+			Name: "print",
+			Type: cty.DynamicPseudoType,
+		},
+	},
+	Type: func(args []cty.Value) (cty.Type, error) {
+		return args[0].Type(), nil
+	},
+	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
+		if b, err := json.Marshal(args[0], args[0].Type()); err != nil {
+			fmt.Println(args[0].GoString())
+		} else {
+			fmt.Println(string(b))
+		}
+		return args[0], nil
+	},
+})


Test patch:
diff --git a/schemahcl/stdlib_test.go b/schemahcl/stdlib_test.go
index 299b3ba2..9e1c4a79 100644
--- a/schemahcl/stdlib_test.go
+++ b/schemahcl/stdlib_test.go
@@ -108,3 +108,38 @@ func TestURLEscapeFunc(t *testing.T) {
 		})
 	}
 }
+
+func Example_PrintFunc() {
+	for _, f := range []string{
+		`v  = print(1)`,
+		`v  = print(true)`,
+		`v  = print("hello, world")`,
+		`v  = print({"hello": "world"})`,
+		`v  = print(["hello", "world"])`,
+	} {
+		var d struct {
+			V cty.Value `spec:"v"`
+		}
+		if err := New().EvalBytes([]byte(f), &d, nil); err != nil {
+			fmt.Println("failed to evaluate:", err)
+			return
+		}
+		fmt.Printf("%#v\n\n", d.V)
+	}
+	// Output:
+	// 1
+	// cty.NumberIntVal(1)
+	//
+	// true
+	// cty.True
+	//
+	// "hello, world"
+	// cty.StringVal("hello, world")
+	//
+	// {"hello":"world"}
+	// cty.ObjectVal(map[string]cty.Value{"hello":cty.StringVal("world")})
+	//
+	// ["hello","world"]
+	// cty.ListVal([]cty.Value{cty.StringVal("hello"), cty.StringVal("world")})
+	//
+}
Repository: ariga/atlas
Pull request #1840 (2023-07-09 20:20:26)

schemahcl: allow reference blocks in template expressions 
This helps catching invalid statements before executing them on dev-database


Patch:
diff --git a/schemahcl/context.go b/schemahcl/context.go
index c37a57b5..be60bc0a 100644
--- a/schemahcl/context.go
+++ b/schemahcl/context.go
@@ -291,6 +291,10 @@ func blockVars(blocks hclsyntax.Blocks, parentAddr string, defs *blockDef) (map[
 			}
 			self := addr(parentAddr, name, blkName, qualifier)
 			attrs["__ref"] = cty.StringVal(self)
+			// Skip naming blocks with "name" attribute.
+			if _, ok := blk.Body.Attributes["name"]; !ok {
+				attrs["name"] = cty.StringVal(blkName)
+			}
 			varMap, err := blockVars(blk.Body.Blocks, self, def)
 			if err != nil {
 				return nil, err
@@ -456,6 +460,7 @@ func (t *blockDef) asCty() cty.Type {
 	for attr := range t.fields {
 		f[attr] = ctyNilType
 	}
+	f["name"] = cty.String
 	f["__ref"] = cty.String
 	for _, c := range t.children {
 		f[c.name] = c.asCty()


Test patch:
diff --git a/schemahcl/context_test.go b/schemahcl/context_test.go
index 2b3fa45b..fc7b944d 100644
--- a/schemahcl/context_test.go
+++ b/schemahcl/context_test.go
@@ -6,6 +6,7 @@ package schemahcl
 
 import (
 	"reflect"
+	"strings"
 	"testing"
 
 	"github.com/stretchr/testify/require"
@@ -612,3 +613,35 @@ variable "name" {
 	err = New().EvalBytes([]byte(h), &struct{}{}, nil)
 	require.EqualError(t, err, `invalid type "boring" for variable "name". Valid types are: string, number, bool, list, map, or set`)
 }
+
+func TestTemplateReferences(t *testing.T) {
+	var (
+		d struct {
+			Stmt1 string `spec:"stmt1"`
+			Stmt2 string `spec:"stmt2"`
+		}
+		b = []byte(`
+table "foo" {}
+
+table "bar" {
+  name = "baz"
+  column "id" {
+    type = int
+  }
+}
+
+stmt1 = <<-SQL
+   SELECT * FROM ${table.foo.name}
+  SQL
+stmt2 = <<-SQL
+   SELECT ${table.bar.column.id.name} FROM ${table.bar.name}
+  SQL
+`)
+	)
+	require.NoError(t, New().EvalBytes(b, &d, nil))
+	require.Equal(t, "SELECT * FROM foo", strings.TrimSpace(d.Stmt1))
+	require.Equal(t, "SELECT id FROM baz", strings.TrimSpace(d.Stmt2))
+
+	err := New().EvalBytes([]byte(`v = "${unknown}"`), &d, nil)
+	require.EqualError(t, err, `:1,8-15: Unknown variable; There is no variable named "unknown".`)
+}
Repository: ariga/atlas
Pull request #2294 (2023-11-20 11:39:03)

sql/internal/sqlx: add Builder.WrapErr


Patch:
diff --git a/sql/internal/sqlx/sqlx.go b/sql/internal/sqlx/sqlx.go
index 270da7f9..6f8b2699 100644
--- a/sql/internal/sqlx/sqlx.go
+++ b/sql/internal/sqlx/sqlx.go
@@ -441,6 +441,13 @@ func (b *Builder) Wrap(f func(b *Builder)) *Builder {
 	return b
 }
 
+// WrapErr wraps the written string with parentheses
+func (b *Builder) WrapErr(f func(b *Builder) error) error {
+	var err error
+	b.Wrap(func(b *Builder) { err = f(b) })
+	return err
+}
+
 // WrapIndent is like Wrap but with extra level of indentation.
 func (b *Builder) WrapIndent(f func(b *Builder)) *Builder {
 	return b.Wrap(func(b *Builder) {


Test patch:
diff --git a/sql/internal/sqlx/sqlx_test.go b/sql/internal/sqlx/sqlx_test.go
index e26b9821..db6de553 100644
--- a/sql/internal/sqlx/sqlx_test.go
+++ b/sql/internal/sqlx/sqlx_test.go
@@ -5,6 +5,7 @@
 package sqlx
 
 import (
+	"errors"
 	"strconv"
 	"testing"
 
@@ -59,6 +60,9 @@ func TestBuilder(t *testing.T) {
 			})
 		})
 	require.Equal(t, `CREATE TABLE "users" ("a" int NOT NULL, "b" int NOT NULL, "c" int NOT NULL, PRIMARY KEY ("a", "b", "c"))`, b.String())
+
+	// WrapErr.
+	require.EqualError(t, b.WrapErr(func(*Builder) error { return errors.New("oops") }), "oops")
 }
 
 func TestBuilder_Qualifier(t *testing.T) {
Repository: ariga/atlas
Pull request #2339 (2023-12-05 21:13:18)

sql/internal/sqlx: diff realm adds new schema with all its resources
Fixed https://github.com/ariga/atlas/issues/2156


Patch:
diff --git a/sql/internal/sqlx/diff.go b/sql/internal/sqlx/diff.go
index 22efcade..5afb647e 100644
--- a/sql/internal/sqlx/diff.go
+++ b/sql/internal/sqlx/diff.go
@@ -122,6 +122,15 @@ func (d *Diff) RealmDiff(from, to *schema.Realm, options ...schema.DiffOption) (
 			continue
 		}
 		changes = opts.AddOrSkip(changes, &schema.AddSchema{S: s1})
+		for _, o := range s1.Objects {
+			changes = opts.AddOrSkip(changes, &schema.AddObject{O: o})
+		}
+		for _, f := range s1.Funcs {
+			changes = opts.AddOrSkip(changes, &schema.AddFunc{F: f})
+		}
+		for _, p := range s1.Procs {
+			changes = opts.AddOrSkip(changes, &schema.AddProc{P: p})
+		}
 		for _, t := range s1.Tables {
 			changes = opts.AddOrSkip(changes, &schema.AddTable{T: t})
 		}


Test patch:
diff --git a/sql/postgres/diff_test.go b/sql/postgres/diff_test.go
index 02515d6b..e8763f84 100644
--- a/sql/postgres/diff_test.go
+++ b/sql/postgres/diff_test.go
@@ -484,6 +484,27 @@ func TestDiff_TableDiff(t *testing.T) {
 	}
 }
 
+func TestDiff_RealmDiff(t *testing.T) {
+	db, m, err := sqlmock.New()
+	require.NoError(t, err)
+	mock{m}.version("130000")
+	drv, err := Open(db)
+	to := schema.New("public").
+		AddTables(
+			schema.NewTable("users").AddColumns(schema.NewIntColumn("t2_id", "int")),
+		).
+		AddObjects(
+			&schema.EnumType{T: "e", Values: []string{"b"}},
+		)
+	changes, err := drv.RealmDiff(schema.NewRealm(), schema.NewRealm(to))
+	require.NoError(t, err)
+	require.EqualValues(t, []schema.Change{
+		&schema.AddSchema{S: to},
+		&schema.AddObject{O: to.Objects[0]},
+		&schema.AddTable{T: to.Tables[0]},
+	}, changes)
+}
+
 func TestDiff_SchemaDiff(t *testing.T) {
 	db, m, err := sqlmock.New()
 	require.NoError(t, err)
Repository: ariga/atlas
Pull request #2371 (2023-12-16 07:57:17)

sql/schema: add object deps to triggers


Patch:
diff --git a/sql/internal/sqlx/plan.go b/sql/internal/sqlx/plan.go
index 22ed8e98..fbb3230d 100644
--- a/sql/internal/sqlx/plan.go
+++ b/sql/internal/sqlx/plan.go
@@ -496,6 +496,10 @@ func dependsOn(c1, c2 schema.Change) bool {
 				fk, ok := c.(*schema.DropForeignKey)
 				return ok && fk.F.RefTable == c2.T
 			})
+		case *schema.DropTrigger:
+			if c2.T.Table == c1.T {
+				return true
+			}
 		}
 		return depOfDrop(c1.T, c2)
 	case *schema.ModifyTable:
@@ -549,6 +553,9 @@ func dependsOn(c1, c2 schema.Change) bool {
 			return depOfAdd(c1.V.Deps, c2)
 		}
 	case *schema.DropView:
+		if c2, ok := c2.(*schema.DropTrigger); ok && c2.T.View == c1.V {
+			return true
+		}
 		return depOfDrop(c1.V, c2)
 	case *schema.ModifyView:
 		if c2, ok := c2.(*schema.AddView); ok {
@@ -615,6 +622,15 @@ func dependsOn(c1, c2 schema.Change) bool {
 				return dependsOnT(f.Type, t)
 			})
 		}
+	case *schema.AddTrigger:
+		switch c2 := c2.(type) {
+		case *schema.AddTable:
+			return c1.T.Table == c2.T
+		case *schema.AddView:
+			return c1.T.View == c2.V
+		default:
+			return depOfAdd(c1.T.Deps, c2)
+		}
 	}
 	return false
 }
@@ -665,6 +681,8 @@ func depOfDrop(o schema.Object, c schema.Change) bool {
 		deps = c.F.Deps
 	case *schema.DropProc:
 		deps = c.P.Deps
+	case *schema.DropTrigger:
+		deps = c.T.Deps
 	}
 	return slices.Contains(deps, o)
 }
diff --git a/sql/schema/schema.go b/sql/schema/schema.go
index ca398d4c..9429927a 100644
--- a/sql/schema/schema.go
+++ b/sql/schema/schema.go
@@ -122,6 +122,7 @@ type (
 		For        TriggerFor     // FOR EACH ROW or FOR EACH STATEMENT.
 		Body       string         // Trigger body only.
 		Attrs      []Attr         // WHEN, REFERENCING, etc.
+		Deps       []Object       // Objects this trigger depends on.
 	}
 
 	// TriggerTime represents the trigger action time.
@@ -589,6 +590,7 @@ func (*Table) obj()    {}
 func (*View) obj()     {}
 func (*Func) obj()     {}
 func (*Proc) obj()     {}
+func (*Trigger) obj()  {}
 func (*EnumType) obj() {}
 
 // expressions.


Test patch:
diff --git a/sql/internal/sqlx/plan_test.go b/sql/internal/sqlx/plan_test.go
index a4eddb5e..32f88da6 100644
--- a/sql/internal/sqlx/plan_test.go
+++ b/sql/internal/sqlx/plan_test.go
@@ -166,3 +166,24 @@ func TestBadTypeComparison(t *testing.T) {
 		&schema.DropTable{T: schema.NewTable("t1").AddColumns(schema.NewColumn("c1").SetType(o1))},
 	})
 }
+
+func TestSortChanges(t *testing.T) {
+	t1 := &schema.Table{Name: "t1"}
+	f1 := &schema.Func{Name: "f1", Deps: []schema.Object{t1}}
+	tr1 := &schema.Trigger{Name: "tr", Table: t1, Deps: []schema.Object{f1}}
+	changes := []schema.Change{
+		&schema.AddTrigger{T: tr1},
+		&schema.AddTable{T: t1},
+		&schema.AddFunc{F: f1},
+	}
+	planned := SortChanges(changes)
+	require.Equal(t, []schema.Change{changes[1], changes[2], changes[0]}, planned)
+
+	changes = []schema.Change{
+		&schema.DropTable{T: t1},
+		&schema.DropFunc{F: f1},
+		&schema.DropTrigger{T: tr1},
+	}
+	planned = SortChanges(changes)
+	require.Equal(t, []schema.Change{changes[2], changes[1], changes[0]}, planned)
+}
Repository: ariga/atlas
Pull request #2409 (2023-12-28 12:53:58)

sql/internal/sqlx: indirect drop trigger dropping
Triggers are not explicitly dropped as they are dropped together with the table they attach to


Patch:
diff --git a/sql/internal/sqlx/plan.go b/sql/internal/sqlx/plan.go
index 2a5cab5d..69cdf483 100644
--- a/sql/internal/sqlx/plan.go
+++ b/sql/internal/sqlx/plan.go
@@ -679,8 +679,14 @@ func depOfDrop(o schema.Object, c schema.Change) bool {
 	switch c := c.(type) {
 	case *schema.DropTable:
 		deps = c.T.Deps
+		for _, t := range c.T.Triggers {
+			deps = append(deps, t.Deps...)
+		}
 	case *schema.DropView:
 		deps = c.V.Deps
+		for _, t := range c.V.Triggers {
+			deps = append(deps, t.Deps...)
+		}
 	case *schema.DropFunc:
 		deps = c.F.Deps
 	case *schema.DropProc:


Test patch:
diff --git a/sql/internal/sqlx/plan_test.go b/sql/internal/sqlx/plan_test.go
index 27257ee9..d464d295 100644
--- a/sql/internal/sqlx/plan_test.go
+++ b/sql/internal/sqlx/plan_test.go
@@ -194,4 +194,19 @@ func TestSortChanges(t *testing.T) {
 	}
 	planned = SortChanges(changes)
 	require.Equal(t, []schema.Change{changes[2], changes[1], changes[0]}, planned)
+
+	// No changes.
+	planned = SortChanges([]schema.Change{
+		&schema.DropFunc{F: f1},
+		&schema.DropTable{T: t1},
+	})
+	require.Equal(t, []schema.Change{&schema.DropFunc{F: f1}, &schema.DropTable{T: t1}}, planned)
+
+	// The table must be dropped before the function if one of its triggers depends on the function.
+	t1.Triggers = []*schema.Trigger{tr1}
+	planned = SortChanges([]schema.Change{
+		&schema.DropFunc{F: f1},
+		&schema.DropTable{T: t1},
+	})
+	require.Equal(t, []schema.Change{&schema.DropTable{T: t1}, &schema.DropFunc{F: f1}}, planned)
 }
Repository: ariga/atlas
Pull request #2466 (2024-01-22 18:55:20)

schemahcl: allow set for_each on tuple any


Patch:
diff --git a/doc/md/atlas-schema/hcl.mdx b/doc/md/atlas-schema/hcl.mdx
index aa928fc7..583c4a80 100644
--- a/doc/md/atlas-schema/hcl.mdx
+++ b/doc/md/atlas-schema/hcl.mdx
@@ -805,7 +805,7 @@ or a `set` of references.
 
 ```hcl title="schema.pg.hcl" {2-3}
 trigger "audit_log_trigger" {
-  for_each = toset([table.users, table.orders, table.payments])
+  for_each = [table.users, table.orders, table.payments]
   on       = each.value
   after {
     insert = true
diff --git a/schemahcl/schemahcl.go b/schemahcl/schemahcl.go
index b6cd4446..8635be04 100644
--- a/schemahcl/schemahcl.go
+++ b/schemahcl/schemahcl.go
@@ -942,7 +942,7 @@ func forEachBlocks(ctx *hcl.EvalContext, b *hclsyntax.Block) ([]*hclsyntax.Block
 	if diags.HasErrors() {
 		return nil, diags
 	}
-	if t := forEach.Type(); !t.IsSetType() && !t.IsObjectType() {
+	if t := forEach.Type(); !t.IsSetType() && !t.IsObjectType() && !t.IsTupleType() {
 		return nil, fmt.Errorf("schemahcl: for_each does not support %s type", t.FriendlyName())
 	}
 	delete(b.Body.Attributes, forEachAttr)


Test patch:
diff --git a/schemahcl/schemahcl_test.go b/schemahcl/schemahcl_test.go
index e3ad75ac..b06eadb1 100644
--- a/schemahcl/schemahcl_test.go
+++ b/schemahcl/schemahcl_test.go
@@ -501,6 +501,37 @@ table "t1" {
 table "t2" {
   schema = schema.s2
 }
+`, string(buf))
+
+	// Tuple of type any.
+	b1 = []byte(`
+schema "s1" {
+  comment = "schema comment"
+}
+schema "s2" {
+  # object without comment.
+}
+
+table {
+  for_each = [schema.s1, schema.s2]
+  name = each.value.name
+  schema = each.value
+}
+`)
+	err = New().EvalBytes(b1, &doc1, nil)
+	require.NoError(t, err)
+	buf, err = Marshal.MarshalSpec(&doc1)
+	require.NoError(t, err)
+	require.Equal(t, `schema "s1" {
+}
+schema "s2" {
+}
+table "s1" {
+  schema = schema.s1
+}
+table "s2" {
+  schema = schema.s2
+}
 `, string(buf))
 }
Repository: ariga/atlas
Pull request #2529 (2024-02-09 13:37:21)

schemahcl: ensure references are valid before scan


Patch:
diff --git a/schemahcl/schemahcl.go b/schemahcl/schemahcl.go
index 91cc1705..d7be1dfe 100644
--- a/schemahcl/schemahcl.go
+++ b/schemahcl/schemahcl.go
@@ -545,6 +545,9 @@ func (s *State) toAttrs(ctx *hcl.EvalContext, vr SchemaValidator, hclAttrs hclsy
 			for it := value.ElementIterator(); it.Next(); {
 				_, v := it.Element()
 				if isRef(v) {
+					if !v.Type().HasAttribute("__ref") {
+						return nil, fmt.Errorf("%s: invalid reference used in %s", hclAttr.SrcRange, hclAttr.Name)
+					}
 					v = cty.CapsuleVal(ctyRefType, &Ref{V: v.GetAttr("__ref").AsString()})
 				}
 				if vt != cty.NilType && vt != v.Type() {
@@ -559,7 +562,6 @@ func (s *State) toAttrs(ctx *hcl.EvalContext, vr SchemaValidator, hclAttrs hclsy
 		}
 		attrs = append(attrs, at)
 	}
-	// hclsyntax.Attrs is an alias for map[string]*Attribute
 	sort.Slice(attrs, func(i, j int) bool {
 		return attrs[i].K < attrs[j].K
 	})


Test patch:
diff --git a/schemahcl/schemahcl_test.go b/schemahcl/schemahcl_test.go
index f4f82f85..5119a795 100644
--- a/schemahcl/schemahcl_test.go
+++ b/schemahcl/schemahcl_test.go
@@ -110,6 +110,21 @@ func TestResource(t *testing.T) {
 	require.EqualValues(t, f, string(buf))
 }
 
+func TestInvalidRefs(t *testing.T) {
+	var doc struct {
+		Tables []struct {
+			Name string `spec:",name"`
+			Refs []*Ref `spec:"ref"`
+		}
+	}
+	err := New().EvalBytes([]byte(`
+table "bar" {
+  refs = [table]
+}
+`), &doc, nil)
+	require.EqualError(t, err, ":3,3-17: invalid reference used in refs")
+}
+
 func ExampleUnmarshal() {
 	f := `
 show "seinfeld" {