nebius/SWE-rebench-V2-PRs huggingface dataset:
Repository: ariga/atlas
Pull request #493 (2022-01-25 19:53:42)

sql/mysql: use one form for printing boolean types


Patch:
diff --git a/sql/mysql/convert.go b/sql/mysql/convert.go
index f59d26a7..a326bb06 100644
--- a/sql/mysql/convert.go
+++ b/sql/mysql/convert.go
@@ -23,8 +23,8 @@ func FormatType(t schema.Type) (string, error) {
 	case *schema.BoolType:
 		// Map all flavors to a single form.
 		switch f = strings.ToLower(t.T); f {
-		case "bool", "boolean", "tinyint", "tinyint(1)":
-			f = "bool"
+		case TypeBool, TypeBoolean, TypeTinyInt, "tinyint(1)":
+			f = TypeBool
 		}
 	case *schema.BinaryType:
 		f = strings.ToLower(t.T)
@@ -115,7 +115,7 @@ func ParseType(raw string) (schema.Type, error) {
 	case TypeTinyInt, TypeSmallInt, TypeMediumInt, TypeInt, TypeBigInt:
 		if size == 1 {
 			return &schema.BoolType{
-				T: t,
+				T: TypeBool,
 			}, nil
 		}
 		// For integer types, the size represents the display width and does not


Test patch:
diff --git a/sql/mysql/inspect_test.go b/sql/mysql/inspect_test.go
index 5422fb35..d830f8a8 100644
--- a/sql/mysql/inspect_test.go
+++ b/sql/mysql/inspect_test.go
@@ -145,7 +145,7 @@ func TestDriver_InspectTable(t *testing.T) {
 				require.Equal("users", t.Name)
 				require.EqualValues([]*schema.Column{
 					{Name: "id", Type: &schema.ColumnType{Raw: "bigint(20)", Type: &schema.IntegerType{T: "bigint"}}},
-					{Name: "v57_tiny", Type: &schema.ColumnType{Raw: "tinyint(1)", Type: &schema.BoolType{T: "tinyint"}}},
+					{Name: "v57_tiny", Type: &schema.ColumnType{Raw: "tinyint(1)", Type: &schema.BoolType{T: "bool"}}},
 					{Name: "v57_tiny_unsigned", Type: &schema.ColumnType{Raw: "tinyint(4) unsigned", Type: &schema.IntegerType{T: "tinyint", Unsigned: true}}},
 					{Name: "v57_small", Type: &schema.ColumnType{Raw: "smallint(6)", Type: &schema.IntegerType{T: "smallint"}}},
 					{Name: "v57_small_unsigned", Type: &schema.ColumnType{Raw: "smallint(6) unsigned", Type: &schema.IntegerType{T: "smallint", Unsigned: true}}},
@@ -196,7 +196,7 @@ func TestDriver_InspectTable(t *testing.T) {
 				require.Equal("users", t.Name)
 				require.EqualValues([]*schema.Column{
 					{Name: "id", Type: &schema.ColumnType{Raw: "bigint(20)", Type: &schema.IntegerType{T: "bigint"}}},
-					{Name: "tiny_int", Type: &schema.ColumnType{Raw: "tinyint(1)", Type: &schema.BoolType{T: "tinyint"}}},
+					{Name: "tiny_int", Type: &schema.ColumnType{Raw: "tinyint(1)", Type: &schema.BoolType{T: "bool"}}},
 					{Name: "longtext", Type: &schema.ColumnType{Raw: "longtext", Type: &schema.StringType{T: "longtext"}}},
 					{Name: "jsonc", Type: &schema.ColumnType{Raw: "json", Type: &schema.JSONType{T: "json"}}},
 				}, t.Columns)
diff --git a/sql/mysql/sqlspec_test.go b/sql/mysql/sqlspec_test.go
index 4085c9c9..7b70d2e2 100644
--- a/sql/mysql/sqlspec_test.go
+++ b/sql/mysql/sqlspec_test.go
@@ -806,6 +806,10 @@ func TestTypes(t *testing.T) {
 			typeExpr: "geometrycollection",
 			expected: &schema.SpatialType{T: TypeGeometryCollection},
 		},
+		{
+			typeExpr: "tinyint(1)",
+			expected: &schema.BoolType{T: TypeBool},
+		},
 		{
 			typeExpr: "bool",
 			expected: &schema.BoolType{T: TypeBool},
Repository: ariga/atlas
Pull request #505 (2022-01-27 09:11:14)

schema/schemspec/schemahcl: remove redundant comma when printing lists


Patch:
diff --git a/schema/schemaspec/schemahcl/hcl.go b/schema/schemaspec/schemahcl/hcl.go
index 03f2044e..943db72a 100644
--- a/schema/schemaspec/schemahcl/hcl.go
+++ b/schema/schemaspec/schemahcl/hcl.go
@@ -379,14 +379,11 @@ func hclRawList(items []string) hclwrite.Tokens {
 		Type:  hclsyntax.TokenOBrack,
 		Bytes: []byte("["),
 	}}
-	for _, item := range items {
-		t = append(t, &hclwrite.Token{
-			Type:  hclsyntax.TokenIdent,
-			Bytes: []byte(item),
-		}, &hclwrite.Token{
-			Type:  hclsyntax.TokenComma,
-			Bytes: []byte(","),
-		})
+	for i, item := range items {
+		if i > 0 {
+			t = append(t, &hclwrite.Token{Type: hclsyntax.TokenComma, Bytes: []byte(",")})
+		}
+		t = append(t, &hclwrite.Token{Type: hclsyntax.TokenIdent, Bytes: []byte(item)})
 	}
 	t = append(t, &hclwrite.Token{
 		Type:  hclsyntax.TokenCBrack,


Test patch:
diff --git a/schema/schemaspec/schemahcl/context_test.go b/schema/schemaspec/schemahcl/context_test.go
index 4ef1b292..20c1b771 100644
--- a/schema/schemaspec/schemahcl/context_test.go
+++ b/schema/schemaspec/schemahcl/context_test.go
@@ -323,10 +323,10 @@ func TestSchemaRefParse(t *testing.T) {
 	require.NoError(t, err)
 	expected :=
 		`point {
-  z = [a, ]
+  z = [a]
 }
 point {
-  z = [b, ]
+  z = [b]
 }
 `
 	require.Equal(t, expected, string(b))
diff --git a/schema/schemaspec/schemahcl/hcl_test.go b/schema/schemaspec/schemahcl/hcl_test.go
index 49cb3c95..a086d6c9 100644
--- a/schema/schemaspec/schemahcl/hcl_test.go
+++ b/schema/schemaspec/schemahcl/hcl_test.go
@@ -14,8 +14,8 @@ func TestAttributes(t *testing.T) {
 	f := `i  = 1
 b  = true
 s  = "hello, world"
-sl = ["hello", "world", ]
-bl = [true, false, ]
+sl = ["hello", "world"]
+bl = [true, false]
 `
 	var test struct {
 		Int        int      `spec:"i"`
diff --git a/sql/mysql/sqlspec_test.go b/sql/mysql/sqlspec_test.go
index f72be8f0..2a04eefd 100644
--- a/sql/mysql/sqlspec_test.go
+++ b/sql/mysql/sqlspec_test.go
@@ -415,7 +415,7 @@ func TestMarshalSpec_Comment(t *testing.T) {
   }
   index "index" {
     unique  = true
-    columns = [table.users.column.a, ]
+    columns = [table.users.column.a]
     comment = "index comment"
   }
 }
diff --git a/sql/postgres/sqlspec_test.go b/sql/postgres/sqlspec_test.go
index b93e696f..7f1000b3 100644
--- a/sql/postgres/sqlspec_test.go
+++ b/sql/postgres/sqlspec_test.go
@@ -226,7 +226,7 @@ schema "test" {
 }
 enum "account_type" {
   schema = schema.test
-  values = ["private", "business", ]
+  values = ["private", "business"]
 }
 `
 	require.EqualValues(t, expected, string(buf))
Repository: ariga/atlas
Pull request #586 (2022-02-15 12:51:24)

sql/migrate: fix bug in local dir file write and add tests


Patch:
diff --git a/sql/migrate/migrate.go b/sql/migrate/migrate.go
index d9adefee..2235963d 100644
--- a/sql/migrate/migrate.go
+++ b/sql/migrate/migrate.go
@@ -260,7 +260,7 @@ func (d *LocalDir) Open(name string) (fs.File, error) {
 
 // WriteFile implements Dir.WriteFile.
 func (d *LocalDir) WriteFile(name string, b []byte) error {
-	return os.WriteFile(name, b, 0644)
+	return os.WriteFile(filepath.Join(d.dir, name), b, 0644)
 }
 
 var (


Test patch:
diff --git a/sql/migrate/migrate_test.go b/sql/migrate/migrate_test.go
index 31b6455e..b0004251 100644
--- a/sql/migrate/migrate_test.go
+++ b/sql/migrate/migrate_test.go
@@ -8,7 +8,9 @@ import (
 	"bytes"
 	"context"
 	"database/sql"
+	"io"
 	"io/fs"
+	"os"
 	"strconv"
 	"testing"
 	"text/template"
@@ -107,6 +109,25 @@ func TestGlobStateReader(t *testing.T) {
 	require.Equal(t, drv.executed, []string{"CREATE TABLE t(c int);", "DROP TABLE IF EXISTS t;"})
 }
 
+func TestLocalDir(t *testing.T) {
+	d, err := migrate.NewLocalDir("does_not_exist")
+	require.ErrorIs(t, err, os.ErrNotExist)
+	require.Nil(t, d)
+
+	d, err = migrate.NewLocalDir(t.TempDir())
+	require.NoError(t, err)
+	require.NotNil(t, d)
+	require.NoError(t, d.WriteFile("name", []byte("content")))
+	f, err := d.Open("name")
+	require.NoError(t, err)
+	i, err := f.Stat()
+	require.NoError(t, err)
+	require.Equal(t, i.Name(), "name")
+	c, err := io.ReadAll(f)
+	require.NoError(t, err)
+	require.Equal(t, "content", string(c))
+}
+
 type (
 	mockFS struct {
 		files []*file
Repository: ariga/atlas
Pull request #633 (2022-03-08 10:20:47)

sql/internal/specutil: fix empty ondelete or onupdate in fk


Patch:
diff --git a/sql/internal/specutil/convert.go b/sql/internal/specutil/convert.go
index 5427d9fc..fcb5ebdf 100644
--- a/sql/internal/specutil/convert.go
+++ b/sql/internal/specutil/convert.go
@@ -472,13 +472,18 @@ func FromForeignKey(s *schema.ForeignKey) (*sqlspec.ForeignKey, error) {
 		}
 		r = append(r, ref)
 	}
-	return &sqlspec.ForeignKey{
+	fk := &sqlspec.ForeignKey{
 		Symbol:     s.Symbol,
 		Columns:    c,
 		RefColumns: r,
-		OnUpdate:   &schemaspec.Ref{V: Var(string(s.OnUpdate))},
-		OnDelete:   &schemaspec.Ref{V: Var(string(s.OnDelete))},
-	}, nil
+	}
+	if s.OnUpdate != "" {
+		fk.OnUpdate = &schemaspec.Ref{V: Var(string(s.OnUpdate))}
+	}
+	if s.OnDelete != "" {
+		fk.OnDelete = &schemaspec.Ref{V: Var(string(s.OnDelete))}
+	}
+	return fk, nil
 }
 
 // FromCheck converts schema.Check to sqlspec.Check.


Test patch:
diff --git a/sql/internal/specutil/convert_test.go b/sql/internal/specutil/convert_test.go
index 814d636e..163b6a6d 100644
--- a/sql/internal/specutil/convert_test.go
+++ b/sql/internal/specutil/convert_test.go
@@ -77,4 +77,18 @@ func TestFromForeignKey(t *testing.T) {
 		OnUpdate: &schemaspec.Ref{V: "NO_ACTION"},
 		OnDelete: &schemaspec.Ref{V: "CASCADE"},
 	}, key)
+
+	fk.OnDelete = ""
+	fk.OnUpdate = ""
+	key, err = FromForeignKey(fk)
+	require.NoError(t, err)
+	require.EqualValues(t, &sqlspec.ForeignKey{
+		Symbol: "fk",
+		Columns: []*schemaspec.Ref{
+			{V: "$column.parent_id"},
+		},
+		RefColumns: []*schemaspec.Ref{
+			{V: "$column.id"},
+		},
+	}, key)
 }
Repository: ariga/atlas
Pull request #673 (2022-03-30 18:08:37)

schema/schemaspec: marshal qualifers
* marshal qualifiers to HCL
* schemaspec.Extension Scan handles qualifier fields


Patch:
diff --git a/schema/schemaspec/extension.go b/schema/schemaspec/extension.go
index 1164c1d3..61806243 100644
--- a/schema/schemaspec/extension.go
+++ b/schema/schemaspec/extension.go
@@ -388,6 +388,11 @@ func (r *Resource) Scan(ext interface{}) error {
 				return errors.New("schemaspec: extension name field must be string")
 			}
 			r.Name = field.String()
+		case ft.isQualifier():
+			if field.Kind() != reflect.String {
+				return errors.New("schemaspec: extension qualifer field must be string")
+			}
+			r.Qualifier = field.String()
 		case isResourceSlice(field.Type()):
 			for i := 0; i < field.Len(); i++ {
 				ext := field.Index(i).Interface()
diff --git a/schema/schemaspec/schemahcl/hcl.go b/schema/schemaspec/schemahcl/hcl.go
index 98674d8b..fec55418 100644
--- a/schema/schemaspec/schemahcl/hcl.go
+++ b/schema/schemaspec/schemahcl/hcl.go
@@ -267,6 +267,9 @@ func (s *state) writeResource(b *schemaspec.Resource, body *hclwrite.Body) error
 
 func labels(r *schemaspec.Resource) []string {
 	var l []string
+	if r.Qualifier != "" {
+		l = append(l, r.Qualifier)
+	}
 	if r.Name != "" {
 		l = append(l, r.Name)
 	}


Test patch:
diff --git a/schema/schemaspec/schemahcl/hcl_test.go b/schema/schemaspec/schemahcl/hcl_test.go
index 54e6fb5f..58e8067e 100644
--- a/schema/schemaspec/schemahcl/hcl_test.go
+++ b/schema/schemaspec/schemahcl/hcl_test.go
@@ -236,9 +236,7 @@ func TestQualified(t *testing.T) {
 	var test struct {
 		Person *Person `spec:"person"`
 	}
-	h := `
-person "dr" "jekyll" {
-  
+	h := `person "dr" "jekyll" {
 }
 `
 	err := Unmarshal([]byte(h), &test)
@@ -247,4 +245,7 @@ person "dr" "jekyll" {
 		Title: "dr",
 		Name:  "jekyll",
 	})
+	out, err := Marshal(&test)
+	require.NoError(t, err)
+	require.EqualValues(t, h, string(out))
 }
Repository: ariga/atlas
Pull request #696 (2022-04-11 15:48:58)

internal/tool: add templates for liquibase compatible migration files


Patch:
diff --git a/internal/tool/tool.go b/internal/tool/tool.go
index 9c356f6e..f35d0047 100644
--- a/internal/tool/tool.go
+++ b/internal/tool/tool.go
@@ -14,6 +14,7 @@ import (
 var (
 	// funcs contains the template.FuncMap for the different formatters.
 	funcs = template.FuncMap{
+		"inc": func(x int) int { return x + 1 },
 		// now format the current time in a lexicographically ascending order while maintaining human readability.
 		"now": func() string { return time.Now().Format("20060102150405") },
 		"rev": reverse,
@@ -51,6 +52,22 @@ func NewFlywayFormatter() (migrate.Formatter, error) {
 	)
 }
 
+// NewLiquibaseFormatter returns a migrate.Formatter computable with Liquibase.
+func NewLiquibaseFormatter() (migrate.Formatter, error) { // TODO(masseelch): add dbms property (meta data needed)
+	return templateFormatter(
+		"{{ now }}{{ with .Name }}_{{ . }}{{ end }}.sql",
+		`{{- $now := now -}}
+--liquibase formatted sql
+
+{{- range $index, $change := .Changes }}
+--changeset atlas:{{ $now }}-{{ inc $index }}
+{{ with $change.Comment }}--comment: {{ . }}{{ end }}
+{{ $change.Cmd }};
+{{ with $change.Reverse }}--rollback: {{ . }};{{ end }}
+{{ end }}`,
+	)
+}
+
 // templateFormatter parses the given templates and passes them on to the migrate.NewTemplateFormatter.
 func templateFormatter(templates ...string) (fmt migrate.Formatter, err error) {
 	tpls := make([]*template.Template, len(templates))


Test patch:
diff --git a/internal/tool/tool_test.go b/internal/tool/tool_test.go
index ce1eb3d0..894fcce8 100644
--- a/internal/tool/tool_test.go
+++ b/internal/tool/tool_test.go
@@ -5,6 +5,7 @@
 package tool_test
 
 import (
+	"fmt"
 	"io/fs"
 	"testing"
 	"time"
@@ -80,6 +81,23 @@ DROP TABLE t1 IF EXISTS;
 `,
 			},
 		},
+		{
+			"liquibase",
+			tool.NewLiquibaseFormatter,
+			map[string]string{
+				v + "_tooling-plan.sql": fmt.Sprintf(`--liquibase formatted sql
+--changeset atlas:%s-1
+--comment: create table t1
+CREATE TABLE t1(c int);
+--rollback: DROP TABLE t1 IF EXISTS;
+
+--changeset atlas:%s-2
+--comment: create table t2
+CREATE TABLE t2(c int);
+--rollback: DROP TABLE t2;
+`, v, v),
+			},
+		},
 	} {
 		t.Run(tt.name, func(t *testing.T) {
 			d := dir(t)
Repository: ariga/atlas
Pull request #698 (2022-04-12 12:19:09)

sql/tool: export internal/tool for availability in other packages
E.g. for like Ent.


Patch:
diff --git a/internal/tool/doc.go b/sql/sqltool/doc.go
similarity index 61%
rename from internal/tool/doc.go
rename to sql/sqltool/doc.go
index dacd8ffb..dd055780 100644
--- a/internal/tool/doc.go
+++ b/sql/sqltool/doc.go
@@ -2,5 +2,5 @@
 // This source code is licensed under the Apache 2.0 license found
 // in the LICENSE file in the root directory of this source tree.
 
-// Package tool contains logic to integrate existing tools like Flyway or Liquibase with the Atlas CLI.
-package tool
+// Package sqltool contains logic to integrate existing tools like Flyway or Liquibase with the Atlas CLI.
+package sqltool
diff --git a/internal/tool/tool.go b/sql/sqltool/tool.go
similarity index 99%
rename from internal/tool/tool.go
rename to sql/sqltool/tool.go
index 70cd5d98..91806d43 100644
--- a/internal/tool/tool.go
+++ b/sql/sqltool/tool.go
@@ -2,7 +2,7 @@
 // This source code is licensed under the Apache 2.0 license found
 // in the LICENSE file in the root directory of this source tree.
 
-package tool
+package sqltool
 
 import (
 	"text/template"


Test patch:
diff --git a/internal/tool/tool_test.go b/sql/sqltool/tool_test.go
similarity index 94%
rename from internal/tool/tool_test.go
rename to sql/sqltool/tool_test.go
index 2775f6a5..c8c8fefc 100644
--- a/internal/tool/tool_test.go
+++ b/sql/sqltool/tool_test.go
@@ -2,7 +2,7 @@
 // This source code is licensed under the Apache 2.0 license found
 // in the LICENSE file in the root directory of this source tree.
 
-package tool_test
+package sqltool_test
 
 import (
 	"fmt"
@@ -10,8 +10,8 @@ import (
 	"testing"
 	"time"
 
-	"ariga.io/atlas/internal/tool"
 	"ariga.io/atlas/sql/migrate"
+	"ariga.io/atlas/sql/sqltool"
 	"github.com/stretchr/testify/require"
 )
 
@@ -33,7 +33,7 @@ func TestFormatters(t *testing.T) {
 	}{
 		{
 			"golang-migrate/migrate",
-			tool.GolangMigrateFormatter,
+			sqltool.GolangMigrateFormatter,
 			map[string]string{
 				v + "_tooling-plan.up.sql": `-- create table t1
 CREATE TABLE t1(c int);
@@ -49,7 +49,7 @@ DROP TABLE t1 IF EXISTS;
 		},
 		{
 			"pressly/goose",
-			tool.GooseFormatter,
+			sqltool.GooseFormatter,
 			map[string]string{
 				v + "_tooling-plan.sql": `-- +goose Up
 -- create table t1
@@ -67,7 +67,7 @@ DROP TABLE t1 IF EXISTS;
 		},
 		{
 			"flyway",
-			tool.FlywayFormatter,
+			sqltool.FlywayFormatter,
 			map[string]string{
 				"V" + v + "__tooling-plan.sql": `-- create table t1
 CREATE TABLE t1(c int);
@@ -83,7 +83,7 @@ DROP TABLE t1 IF EXISTS;
 		},
 		{
 			"liquibase",
-			tool.LiquibaseFormatter,
+			sqltool.LiquibaseFormatter,
 			map[string]string{
 				v + "_tooling-plan.sql": fmt.Sprintf(`--liquibase formatted sql
 --changeset atlas:%s-1
Repository: ariga/atlas
Pull request #700 (2022-04-13 07:20:03)

sql/internal/specutil: support input variables


Patch:
diff --git a/sql/internal/specutil/vars.go b/sql/internal/specutil/vars.go
new file mode 100644
index 00000000..557991f8
--- /dev/null
+++ b/sql/internal/specutil/vars.go
@@ -0,0 +1,44 @@
+// Copyright 2021-present The Atlas Authors. All rights reserved.
+// This source code is licensed under the Apache 2.0 license found
+// in the LICENSE file in the root directory of this source tree.
+
+package specutil
+
+import (
+	"ariga.io/atlas/schema/schemaspec"
+	"ariga.io/atlas/schema/schemaspec/schemahcl"
+)
+
+// InputVar is an input variable to an Atlas DDL document.
+type InputVar struct {
+	Name    string                   `spec:",name"`
+	Type    *schemaspec.Type         `spec:"type"`
+	Default *schemaspec.LiteralValue `spec:"default"`
+}
+
+// ExtractVarsHCL extracts the variable definitions from an Atlas DDL HCL document.
+func ExtractVarsHCL(body []byte) ([]*InputVar, error) {
+	var d struct {
+		Vars []*InputVar `spec:"variable"`
+	}
+	if err := hclState.UnmarshalSpec(body, &d); err != nil {
+		return nil, err
+	}
+	return d.Vars, nil
+}
+
+var (
+	hclState = schemahcl.New(schemahcl.WithTypes(
+		NewRegistry(
+			WithSpecs(
+				TypeSpec("int"),
+				TypeSpec("bool"),
+				TypeSpec("float"),
+			),
+		).Specs(),
+	))
+)
+
+func init() {
+	schemaspec.Register("variable", &InputVar{})
+}


Test patch:
diff --git a/sql/internal/specutil/vars_test.go b/sql/internal/specutil/vars_test.go
new file mode 100644
index 00000000..a8a91d07
--- /dev/null
+++ b/sql/internal/specutil/vars_test.go
@@ -0,0 +1,62 @@
+// Copyright 2021-present The Atlas Authors. All rights reserved.
+// This source code is licensed under the Apache 2.0 license found
+// in the LICENSE file in the root directory of this source tree.
+
+package specutil
+
+import (
+	"testing"
+
+	"ariga.io/atlas/schema/schemaspec"
+	"github.com/stretchr/testify/require"
+)
+
+func TestVars(t *testing.T) {
+	h := `variable "age" {
+	type = int
+	default = 42
+}
+
+variable "happy" {
+	type = bool
+	default = true
+}
+
+variable "price" {
+	type = float
+	default = 3.14
+}
+`
+	vars, err := ExtractVarsHCL([]byte(h))
+	require.NoError(t, err)
+	require.Len(t, vars, 3)
+	require.EqualValues(t, []*InputVar{
+		{
+			Name: "age",
+			Type: &schemaspec.Type{
+				T: "int",
+			},
+			Default: &schemaspec.LiteralValue{
+				V: "42",
+			},
+		},
+		{
+			Name: "happy",
+			Type: &schemaspec.Type{
+				T: "bool",
+			},
+			Default: &schemaspec.LiteralValue{
+				V: "true",
+			},
+		},
+		{
+			Name: "price",
+			Type: &schemaspec.Type{
+				T: "float",
+			},
+			Default: &schemaspec.LiteralValue{
+				V: "3.14",
+			},
+		},
+	}, vars)
+}
Repository: ariga/atlas
Pull request #718 (2022-04-17 15:11:53)

sql/postgres: marshal spec duplicate enum entries
When marshalling 2 or more columns that reference the same enum type the generated Atlas DDL document contains duplicate entries for this enum (one entry per column). 


Patch:
diff --git a/sql/postgres/sqlspec.go b/sql/postgres/sqlspec.go
index 2b75d09a..d6e87200 100644
--- a/sql/postgres/sqlspec.go
+++ b/sql/postgres/sqlspec.go
@@ -290,14 +290,19 @@ func schemaSpec(schem *schema.Schema) (*doc, error) {
 	}
 	d.Schemas = []*sqlspec.Schema{s}
 	d.Tables = tbls
+
+	enums := make(map[string]struct{})
 	for _, t := range schem.Tables {
 		for _, c := range t.Columns {
 			if t, ok := c.Type.Type.(*schema.EnumType); ok {
-				d.Enums = append(d.Enums, &Enum{
-					Name:   t.T,
-					Schema: specutil.SchemaRef(s.Name),
-					Values: t.Values,
-				})
+				if _, ok := enums[t.T]; !ok {
+					d.Enums = append(d.Enums, &Enum{
+						Name:   t.T,
+						Schema: specutil.SchemaRef(s.Name),
+						Values: t.Values,
+					})
+					enums[t.T] = struct{}{}
+				}
 			}
 		}
 	}


Test patch:
diff --git a/sql/postgres/sqlspec_test.go b/sql/postgres/sqlspec_test.go
index d89a1be8..e84a2084 100644
--- a/sql/postgres/sqlspec_test.go
+++ b/sql/postgres/sqlspec_test.go
@@ -378,6 +378,13 @@ func TestMarshalSpec_Enum(t *testing.T) {
 						schema.EnumValues("private", "business"),
 					),
 				),
+			schema.NewTable("table2").
+				AddColumns(
+					schema.NewEnumColumn("account_type",
+						schema.EnumName("account_type"),
+						schema.EnumValues("private", "business"),
+					),
+				),
 		)
 	buf, err := MarshalSpec(s, hclState)
 	require.NoError(t, err)
@@ -388,6 +395,13 @@ func TestMarshalSpec_Enum(t *testing.T) {
     type = enum.account_type
   }
 }
+table "table2" {
+  schema = schema.test
+  column "account_type" {
+    null = false
+    type = enum.account_type
+  }
+}
 schema "test" {
 }
 enum "account_type" {