Repository: ariga/atlas
Pull request #1054 (2022-08-15 19:07:44)

sql/postgres: support arrays of type enum
Fixed https://github.com/ariga/atlas/issues/1045


Patch:
diff --git a/sql/internal/sqlx/dev.go b/sql/internal/sqlx/dev.go
index 42d64f81..93736be2 100644
--- a/sql/internal/sqlx/dev.go
+++ b/sql/internal/sqlx/dev.go
@@ -28,6 +28,10 @@ type DevDriver struct {
 	// DropClause holds optional clauses that
 	// can be added to the DropSchema change.
 	DropClause []schema.Clause
+
+	// PatchColumn allows providing a custom function to patch
+	// columns that hold a schema reference.
+	PatchColumn func(*schema.Schema, *schema.Column)
 }
 
 // NormalizeRealm implements the schema.Normalizer interface.
@@ -66,6 +70,9 @@ func (d *DevDriver) NormalizeRealm(ctx context.Context, r *schema.Realm) (nr *sc
 				if e, ok := c.Type.Type.(*schema.EnumType); ok && e.Schema != s {
 					e.Schema = s
 				}
+				if d.PatchColumn != nil {
+					d.PatchColumn(s, c)
+				}
 			}
 			changes = append(changes, &schema.AddTable{T: t})
 		}
diff --git a/sql/postgres/convert.go b/sql/postgres/convert.go
index fd590f36..342e272b 100644
--- a/sql/postgres/convert.go
+++ b/sql/postgres/convert.go
@@ -245,6 +245,11 @@ func columnType(c *columnDesc) (schema.Type, error) {
 			if err != nil {
 				return nil, err
 			}
+			if c.elemtyp == "e" {
+				// Override the element type in
+				// case it is an enum.
+				tt = newEnumType(t, c.typelem)
+			}
 			typ.(*ArrayType).Type = tt
 		}
 	case TypeUserDefined:
@@ -293,6 +298,8 @@ type columnDesc struct {
 	fmtype        string // pg_catalog.format_type
 	size          int64  // character_maximum_length
 	typtype       string // pg_type.typtype
+	typelem       int64  // pg_type.typelem
+	elemtyp       string // pg_type.typtype of the array element type above.
 	typid         int64  // pg_type.oid
 	precision     int64
 	timePrecision *int64
diff --git a/sql/postgres/crdb.go b/sql/postgres/crdb.go
index 37b65065..5df44fae 100644
--- a/sql/postgres/crdb.go
+++ b/sql/postgres/crdb.go
@@ -324,6 +324,8 @@ SELECT
 	t1.generation_expression,
 	col_description(t3.oid, "ordinal_position") AS comment,
 	t4.typtype,
+	t4.typelem,
+	(CASE WHEN t4.typcategory = 'A' AND t4.typelem <> 0 THEN (SELECT t.typtype FROM pg_catalog.pg_type t WHERE t.oid = t4.typelem) END) AS elemtyp,
 	t4.oid
 FROM
 	"information_schema"."columns" AS t1
diff --git a/sql/postgres/diff.go b/sql/postgres/diff.go
index 5b3e541d..c1b850bb 100644
--- a/sql/postgres/diff.go
+++ b/sql/postgres/diff.go
@@ -231,6 +231,10 @@ func (d *diff) typeChanged(from, to *schema.Column) (bool, error) {
 		toT := toT.(*ArrayType)
 		// Same type.
 		if changed = fromT.T != toT.T; !changed {
+			// In case it is an enum type, compare its values.
+			fromE, ok1 := fromT.Type.(*schema.EnumType)
+			toE, ok2 := toT.Type.(*schema.EnumType)
+			changed = ok1 && ok2 && !sqlx.ValuesEqual(fromE.Values, toE.Values)
 			break
 		}
 		// In case the desired schema is not normalized, the string type can look different even
diff --git a/sql/postgres/driver.go b/sql/postgres/driver.go
index 6592bfe3..c9117688 100644
--- a/sql/postgres/driver.go
+++ b/sql/postgres/driver.go
@@ -118,7 +118,15 @@ func Open(db schema.ExecQuerier) (migrate.Driver, error) {
 }
 
 func (d *Driver) dev() *sqlx.DevDriver {
-	return &sqlx.DevDriver{Driver: d, MaxNameLen: 63}
+	return &sqlx.DevDriver{
+		Driver:     d,
+		MaxNameLen: 63,
+		PatchColumn: func(s *schema.Schema, c *schema.Column) {
+			if e, ok := hasEnumType(c); ok {
+				e.Schema = s
+			}
+		},
+	}
 }
 
 // NormalizeRealm returns the normal representation of the given database.
diff --git a/sql/postgres/inspect.go b/sql/postgres/inspect.go
index cdeef200..f28e5999 100644
--- a/sql/postgres/inspect.go
+++ b/sql/postgres/inspect.go
@@ -172,12 +172,12 @@ func (i *inspect) columns(ctx context.Context, s *schema.Schema) error {
 // addColumn scans the current row and adds a new column from it to the table.
 func (i *inspect) addColumn(s *schema.Schema, rows *sql.Rows) (err error) {
 	var (
-		typid, maxlen, precision, timeprecision, scale, seqstart, seqinc, seqlast                                                  sql.NullInt64
-		table, name, typ, fmtype, nullable, defaults, identity, genidentity, genexpr, charset, collate, comment, typtype, interval sql.NullString
+		typid, typelem, maxlen, precision, timeprecision, scale, seqstart, seqinc, seqlast                                                  sql.NullInt64
+		table, name, typ, fmtype, nullable, defaults, identity, genidentity, genexpr, charset, collate, comment, typtype, elemtyp, interval sql.NullString
 	)
 	if err = rows.Scan(
-		&table, &name, &typ, &fmtype, &nullable, &defaults, &maxlen, &precision, &timeprecision, &scale, &interval,
-		&charset, &collate, &identity, &seqstart, &seqinc, &seqlast, &genidentity, &genexpr, &comment, &typtype, &typid,
+		&table, &name, &typ, &fmtype, &nullable, &defaults, &maxlen, &precision, &timeprecision, &scale, &interval, &charset,
+		&collate, &identity, &seqstart, &seqinc, &seqlast, &genidentity, &genexpr, &comment, &typtype, &typelem, &elemtyp, &typid,
 	); err != nil {
 		return err
 	}
@@ -198,6 +198,8 @@ func (i *inspect) addColumn(s *schema.Schema, rows *sql.Rows) (err error) {
 		size:          maxlen.Int64,
 		scale:         scale.Int64,
 		typtype:       typtype.String,
+		typelem:       typelem.Int64,
+		elemtyp:       elemtyp.String,
 		typid:         typid.Int64,
 		interval:      interval.String,
 		precision:     precision.Int64,
@@ -240,22 +242,31 @@ func (i *inspect) enumValues(ctx context.Context, s *schema.Schema) error {
 		args  []interface{}
 		ids   = make(map[int64][]*schema.EnumType)
 		query = "SELECT enumtypid, enumlabel FROM pg_enum WHERE enumtypid IN (%s)"
+		newE  = func(e1 *enumType) *schema.EnumType {
+			if _, ok := ids[e1.ID]; !ok {
+				args = append(args, e1.ID)
+			}
+			// Convert the intermediate type to
+			// the standard schema.EnumType.
+			e2 := &schema.EnumType{T: e1.T, Schema: s}
+			if e1.Schema != "" && e1.Schema != s.Name {
+				e2.Schema = schema.New(e1.Schema)
+			}
+			ids[e1.ID] = append(ids[e1.ID], e2)
+			return e2
+		}
 	)
 	for _, t := range s.Tables {
 		for _, c := range t.Columns {
-			if enum, ok := c.Type.Type.(*enumType); ok {
-				if _, ok := ids[enum.ID]; !ok {
-					args = append(args, enum.ID)
-				}
-				// Convert the intermediate type to
-				// the standard schema.EnumType.
-				e := &schema.EnumType{T: enum.T, Schema: s}
-				if enum.Schema != "" && enum.Schema != s.Name {
-					e.Schema = schema.New(enum.Schema)
-				}
+			switch t := c.Type.Type.(type) {
+			case *enumType:
+				e := newE(t)
 				c.Type.Type = e
-				c.Type.Raw = enum.T
-				ids[enum.ID] = append(ids[enum.ID], e)
+				c.Type.Raw = e.T
+			case *ArrayType:
+				if e, ok := t.Type.(*enumType); ok {
+					t.Type = newE(e)
+				}
 			}
 		}
 	}
@@ -938,6 +949,8 @@ SELECT
 	t1.generation_expression,
 	col_description(t3.oid, "ordinal_position") AS comment,
 	t4.typtype,
+	t4.typelem,
+	(CASE WHEN t4.typcategory = 'A' AND t4.typelem <> 0 THEN (SELECT t.typtype FROM pg_catalog.pg_type t WHERE t.oid = t4.typelem) END) AS elemtyp,
 	t4.oid
 FROM
 	"information_schema"."columns" AS t1
diff --git a/sql/postgres/migrate.go b/sql/postgres/migrate.go
index 4056fe1c..e22c26cc 100644
--- a/sql/postgres/migrate.go
+++ b/sql/postgres/migrate.go
@@ -285,8 +285,8 @@ func (s *state) modifyTable(ctx context.Context, modify *schema.ModifyTable) err
 					continue
 				}
 			}
-			from, ok1 := change.From.Type.Type.(*schema.EnumType)
-			to, ok2 := change.To.Type.Type.(*schema.EnumType)
+			from, ok1 := hasEnumType(change.From)
+			to, ok2 := hasEnumType(change.To)
 			switch {
 			// Enum was changed (underlying values).
 			case ok1 && ok2 && s.enumIdent(modify.T.Schema, from) == s.enumIdent(modify.T.Schema, to):
@@ -359,8 +359,8 @@ func (s *state) alterTable(t *schema.Table, changes []schema.Change) error {
 					To:     change.From,
 					Change: change.Change & ^schema.ChangeGenerated,
 				})
-				toE, toHas := change.From.Type.Type.(*schema.EnumType)
-				fromE, fromHas := change.From.Type.Type.(*schema.EnumType)
+				toE, toHas := hasEnumType(change.To)
+				fromE, fromHas := hasEnumType(change.From)
 				// In case the enum was dropped or replaced with a different one.
 				if fromHas && !toHas || fromHas && toHas && s.enumIdent(t.Schema, fromE) != s.enumIdent(t.Schema, toE) {
 					if err := s.mayDropEnum(alter, t.Schema, fromE); err != nil {
@@ -370,7 +370,7 @@ func (s *state) alterTable(t *schema.Table, changes []schema.Change) error {
 			case *schema.DropColumn:
 				b.P("DROP COLUMN").Ident(change.C.Name)
 				reverse = append(reverse, &schema.AddColumn{C: change.C})
-				if e, ok := change.C.Type.Type.(*schema.EnumType); ok {
+				if e, ok := hasEnumType(change.C); ok {
 					if err := s.mayDropEnum(alter, t.Schema, e); err != nil {
 						return err
 					}
@@ -675,7 +675,7 @@ func (s *state) dropIndexes(t *schema.Table, indexes ...*schema.Index) {
 
 func (s *state) mayAddEnums(ctx context.Context, t *schema.Table, columns ...*schema.Column) error {
 	for _, c := range columns {
-		e, ok := c.Type.Type.(*schema.EnumType)
+		e, ok := hasEnumType(c)
 		if !ok {
 			continue
 		}
@@ -761,7 +761,7 @@ func (s *state) mayDropEnum(alter *alterChange, ns *schema.Schema, e *schema.Enu
 	for i := range schemas {
 		for _, t := range schemas[i].Tables {
 			for _, c := range t.Columns {
-				e1, ok := c.Type.Type.(*schema.EnumType)
+				e1, ok := hasEnumType(c)
 				// Although we search in siblings schemas, use the
 				// table's one for building the enum identifier.
 				if ok && s.enumIdent(ns, e1) == name {
@@ -812,13 +812,8 @@ func (s *state) addIndexes(t *schema.Table, indexes ...*schema.Index) {
 }
 
 func (s *state) column(b *sqlx.Builder, t *schema.Table, c *schema.Column) error {
-	var (
-		f   string
-		err error
-	)
-	if e, ok := c.Type.Type.(*schema.EnumType); ok {
-		f = s.enumIdent(t.Schema, e)
-	} else if f, err = FormatType(c.Type.Type); err != nil {
+	f, err := s.formatType(t, c)
+	if err != nil {
 		return err
 	}
 	b.Ident(c.Name).P(f)
@@ -1146,3 +1141,28 @@ func (s *state) schemaPrefix(ns *schema.Schema) string {
 	}
 	return ""
 }
+
+// formatType formats the type but takes into account the qualifier.
+func (s *state) formatType(t *schema.Table, c *schema.Column) (string, error) {
+	switch tt := c.Type.Type.(type) {
+	case *schema.EnumType:
+		return s.enumIdent(t.Schema, tt), nil
+	case *ArrayType:
+		if e, ok := tt.Type.(*schema.EnumType); ok {
+			return s.enumIdent(t.Schema, e) + "[]", nil
+		}
+	}
+	return FormatType(c.Type.Type)
+}
+
+func hasEnumType(c *schema.Column) (*schema.EnumType, bool) {
+	switch t := c.Type.Type.(type) {
+	case *schema.EnumType:
+		return t, true
+	case *ArrayType:
+		if e, ok := t.Type.(*schema.EnumType); ok {
+			return e, true
+		}
+	}
+	return nil, false
+}
diff --git a/sql/postgres/sqlspec.go b/sql/postgres/sqlspec.go
index 71487918..1a2c2533 100644
--- a/sql/postgres/sqlspec.go
+++ b/sql/postgres/sqlspec.go
@@ -351,19 +351,27 @@ func convertEnums(tables []*sqlspec.Table, enums []*Enum, r *schema.Realm) error
 	}
 	for _, t := range tables {
 		for _, c := range t.Columns {
-			if !c.Type.IsRef {
-				continue
-			}
-			name, err := enumName(c.Type)
-			if err != nil {
-				return err
-			}
-			e, ok := byName[name]
-			if !ok {
-				return fmt.Errorf("enum %q was not found", name)
+			var enum *Enum
+			switch {
+			case c.Type.IsRef:
+				n, err := enumName(c.Type)
+				if err != nil {
+					return err
+				}
+				e, ok := byName[n]
+				if !ok {
+					return fmt.Errorf("enum %q was not found", n)
+				}
+				enum = e
+			default:
+				n, ok := arrayType(c.Type.T)
+				if !ok || byName[n] == nil {
+					continue
+				}
+				enum = byName[n]
 			}
-			used[e] = struct{}{}
-			schemaE, err := specutil.SchemaName(e.Schema)
+			used[enum] = struct{}{}
+			schemaE, err := specutil.SchemaName(enum.Schema)
 			if err != nil {
 				return fmt.Errorf("extract schema name from enum refrence: %w", err)
 			}
@@ -387,7 +395,13 @@ func convertEnums(tables []*sqlspec.Table, enums []*Enum, r *schema.Realm) error
 			if !ok {
 				return fmt.Errorf("column %q not found in table %q", c.Name, t.Name)
 			}
-			cc.Type.Type = &schema.EnumType{T: e.Name, Schema: es, Values: e.Values}
+			e := &schema.EnumType{T: enum.Name, Schema: es, Values: enum.Values}
+			switch t := cc.Type.Type.(type) {
+			case *ArrayType:
+				t.Type = e
+			default:
+				cc.Type.Type = e
+			}
 		}
 	}
 	for _, e := range enums {
@@ -416,30 +430,28 @@ func enumRef(n string) *schemahcl.Ref {
 
 // schemaSpec converts from a concrete Postgres schema to Atlas specification.
 func schemaSpec(schem *schema.Schema) (*doc, error) {
-	var d doc
 	s, tbls, err := specutil.FromSchema(schem, tableSpec)
 	if err != nil {
 		return nil, err
 	}
-	d.Schemas = []*sqlspec.Schema{s}
-	d.Tables = tbls
-
-	enums := make(map[string]struct{})
+	d := &doc{
+		Tables:  tbls,
+		Schemas: []*sqlspec.Schema{s},
+	}
+	enums := make(map[string]bool)
 	for _, t := range schem.Tables {
 		for _, c := range t.Columns {
-			if t, ok := c.Type.Type.(*schema.EnumType); ok {
-				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{}{}
-				}
+			if e, ok := hasEnumType(c); ok && !enums[e.T] {
+				d.Enums = append(d.Enums, &Enum{
+					Name:   e.T,
+					Schema: specutil.SchemaRef(s.Name),
+					Values: e.Values,
+				})
+				enums[e.T] = true
 			}
 		}
 	}
-	return &d, nil
+	return d, nil
 }
 
 // tableSpec converts from a concrete Postgres sqlspec.Table to a schema.Table.


Test patch:
diff --git a/internal/integration/script_test.go b/internal/integration/script_test.go
index fb7e8383..43f6374f 100644
--- a/internal/integration/script_test.go
+++ b/internal/integration/script_test.go
@@ -153,10 +153,7 @@ var (
 	keyDrv *sqlite.Driver
 )
 
-const (
-	atlasPathKey   = "cli.atlas"
-	atlasciPathKey = "cli.atlasci"
-)
+const atlasPathKey = "cli.atlas"
 
 func (t *liteTest) setupScript(env *testscript.Env) error {
 	db, err := sql.Open("sqlite3", fmt.Sprintf("file:%s?cache=shared&_fk=1",
diff --git a/internal/integration/testdata/postgres/column-enum-array.txt b/internal/integration/testdata/postgres/column-enum-array.txt
new file mode 100644
index 00000000..70142271
--- /dev/null
+++ b/internal/integration/testdata/postgres/column-enum-array.txt
@@ -0,0 +1,155 @@
+# Create table with an enum array column.
+apply 1.hcl
+cmpshow enums 1.sql
+
+# Drop an enum array column.
+apply 2.hcl
+cmpshow enums 2.sql
+
+# Append an enum array column to an existing table.
+apply 3.hcl
+cmpshow enums 3.sql
+
+# Append an enum column to existing enum.
+apply 4.hcl
+cmpshow enums 4.sql
+
+# Append an enum value.
+apply 5.hcl
+cmphcl 5.inspect.hcl
+
+-- 1.hcl --
+schema "$db" {}
+
+enum "status" {
+    schema = schema.$db
+    values = ["active", "inactive"]
+}
+
+table "enums" {
+  schema = schema.$db
+  column "statuses" {
+    type = sql("status[]")
+  }
+}
+
+-- 1.sql --
+                    Table "script_column_enum_array.enums"
+  Column  |               Type                | Collation | Nullable | Default
+----------+-----------------------------------+-----------+----------+---------
+ statuses | script_column_enum_array.status[] |           | not null |
+
+-- 2.hcl --
+schema "$db" {}
+
+table "enums" {
+  schema = schema.$db
+  column "a" {
+    type = int
+  }
+}
+
+-- 2.sql --
+      Table "script_column_enum_array.enums"
+ Column |  Type   | Collation | Nullable | Default
+--------+---------+-----------+----------+---------
+ a      | integer |           | not null |
+
+-- 3.hcl --
+schema "$db" {}
+
+enum "status" {
+    schema = schema.$db
+    values = ["active", "inactive"]
+}
+
+table "enums" {
+  schema = schema.$db
+  column "a" {
+    type = int
+  }
+  column "statuses" {
+    type = sql("status[]")
+  }
+}
+
+-- 3.sql --
+                    Table "script_column_enum_array.enums"
+  Column  |               Type                | Collation | Nullable | Default
+----------+-----------------------------------+-----------+----------+---------
+ a        | integer                           |           | not null |
+ statuses | script_column_enum_array.status[] |           | not null |
+
+-- 4.hcl --
+schema "$db" {}
+
+enum "status" {
+    schema = schema.$db
+    values = ["active", "inactive"]
+}
+
+table "enums" {
+  schema = schema.$db
+  column "a" {
+    type = int
+  }
+  column "statuses" {
+    type = sql("status[]")
+  }
+  column "status" {
+    type = enum.status
+  }
+}
+
+-- 4.sql --
+                    Table "script_column_enum_array.enums"
+  Column  |               Type                | Collation | Nullable | Default
+----------+-----------------------------------+-----------+----------+---------
+ a        | integer                           |           | not null |
+ statuses | script_column_enum_array.status[] |           | not null |
+ status   | script_column_enum_array.status   |           | not null |
+
+
+-- 5.hcl --
+schema "$db" {}
+
+enum "status" {
+    schema = schema.$db
+    values = ["active", "inactive", "unknown"]
+}
+
+table "enums" {
+  schema = schema.$db
+  column "a" {
+    type = int
+  }
+  column "statuses" {
+    type = sql("status[]")
+  }
+  column "status" {
+    type = enum.status
+  }
+}
+
+-- 5.inspect.hcl --
+table "enums" {
+  schema = schema.script_column_enum_array
+  column "a" {
+    null = false
+    type = integer
+  }
+  column "statuses" {
+    null = false
+    type = sql("script_column_enum_array.status[]")
+  }
+  column "status" {
+    null = false
+    type = enum.status
+  }
+}
+enum "status" {
+  schema = schema.script_column_enum_array
+  values = ["active", "inactive", "unknown"]
+}
+schema "script_column_enum_array" {
+}
diff --git a/sql/postgres/diff_test.go b/sql/postgres/diff_test.go
index d6ef78f8..ed530d2c 100644
--- a/sql/postgres/diff_test.go
+++ b/sql/postgres/diff_test.go
@@ -284,6 +284,37 @@ func TestDiff_TableDiff(t *testing.T) {
 				},
 			}
 		}(),
+		// Modify array of type enum.
+		func() testcase {
+			var (
+				from = schema.NewTable("users").
+					SetSchema(schema.New("public")).
+					AddColumns(
+						schema.NewColumn("a1").SetType(&ArrayType{T: "state[]", Type: &schema.EnumType{T: "state", Values: []string{"on"}}}),
+						schema.NewColumn("a2").SetType(&ArrayType{T: "state[]", Type: &schema.EnumType{T: "state", Values: []string{"on", "off"}}}),
+						schema.NewColumn("a3").SetType(&ArrayType{T: "state[]", Type: &schema.EnumType{T: "state", Values: []string{"on", "off"}}}),
+					)
+				to = schema.NewTable("users").
+					SetSchema(schema.New("public")).
+					AddColumns(
+						// Add value.
+						schema.NewColumn("a1").SetType(&ArrayType{T: "state[]", Type: &schema.EnumType{T: "state", Values: []string{"on", "off"}}}),
+						// Drop value.
+						schema.NewColumn("a2").SetType(&ArrayType{T: "state[]", Type: &schema.EnumType{T: "state", Values: []string{"on"}}}),
+						// Same values.
+						schema.NewColumn("a3").SetType(&ArrayType{T: "state[]", Type: &schema.EnumType{T: "state", Values: []string{"on", "off"}}}),
+					)
+			)
+			return testcase{
+				name: "enum arrays",
+				from: from,
+				to:   to,
+				wantChanges: []schema.Change{
+					&schema.ModifyColumn{From: from.Columns[0], To: to.Columns[0], Change: schema.ChangeType},
+					&schema.ModifyColumn{From: from.Columns[1], To: to.Columns[1], Change: schema.ChangeType},
+				},
+			}
+		}(),
 		func() testcase {
 			var (
 				from = &schema.Table{
diff --git a/sql/postgres/inspect_test.go b/sql/postgres/inspect_test.go
index 13ac5db2..cc70c396 100644
--- a/sql/postgres/inspect_test.go
+++ b/sql/postgres/inspect_test.go
@@ -41,43 +41,44 @@ func TestDriver_InspectTable(t *testing.T) {
 				m.ExpectQuery(queryColumns).
 					WithArgs("public", "users").
 					WillReturnRows(sqltest.Rows(`
- table_name  |  column_name |          data_type          |  formatted          | is_nullable |         column_default                 | character_maximum_length | numeric_precision | datetime_precision | numeric_scale |    interval_type    | character_set_name | collation_name | is_identity | identity_start | identity_increment |   identity_last  | identity_generation | generation_expression | comment | typtype |  oid
--------------+--------------+-----------------------------+---------------------|-------------+----------------------------------------+--------------------------+-------------------+--------------------+---------------+---------------------+--------------------+----------------+-------------+----------------+--------------------+------------------+---------------------+-----------------------+---------+---------+-------
- users       |  id          | bigint                      | int8                | NO          |                                        |                          |                64 |                    |             0 |                     |                    |                | YES         |      100       |          1         |          1       |    BY DEFAULT       |                       |         | b       |    20
- users       |  rank        | integer                     | int4                | YES         |                                        |                          |                32 |                    |             0 |                     |                    |                | NO          |                |                    |                  |                     |                       | rank    | b       |    23
- users       |  c1          | smallint                    | int2                | NO          |           1000                         |                          |                16 |                    |             0 |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |    21
- users       |  c2          | bit                         | bit                 | NO          |                                        |                        1 |                   |                    |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |  1560
- users       |  c3          | bit varying                 | varbit              | NO          |                                        |                       10 |                   |                    |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |  1562
- users       |  c4          | boolean                     | bool                | NO          |                                        |                          |                   |                    |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |    16
- users       |  c5          | bytea                       | bytea               | NO          |                                        |                          |                   |                    |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |    17
- users       |  c6          | character                   | bpchar              | NO          |                                        |                      100 |                   |                    |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |  1042
- users       |  c7          | character varying           | varchar             | NO          | 'logged_in'::character varying         |                          |                   |                    |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |  1043
- users       |  c8          | cidr                        | cidr                | NO          |                                        |                          |                   |                    |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |   650
- users       |  c9          | circle                      | circle              | NO          |                                        |                          |                   |                    |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |   718
- users       |  c10         | date                        | date                | NO          |                                        |                          |                   |                    |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |  1082
- users       |  c11         | time with time zone         | timetz              | NO          |                                        |                          |                   |                    |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |  1266
- users       |  c12         | double precision            | float8              | NO          |                                        |                          |                53 |                    |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |   701
- users       |  c13         | real                        | float4              | NO          |           random()                     |                          |                24 |                    |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |   700
- users       |  c14         | json                        | json                | NO          |           '{}'::json                   |                          |                   |                    |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |   114
- users       |  c15         | jsonb                       | jsonb               | NO          |           '{}'::jsonb                  |                          |                   |                    |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |  3802
- users       |  c16         | money                       | money               | NO          |                                        |                          |                   |                    |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |   790
- users       |  c17         | numeric                     | numeric             | NO          |                                        |                          |                   |                    |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |  1700
- users       |  c18         | numeric                     | numeric             | NO          |                                        |                          |                 4 |                    |             4 |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |  1700
- users       |  c19         | integer                     | int4                | NO          | nextval('t1_c19_seq'::regclass)        |                          |                32 |                    |             0 |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |    23
- users       |  c20         | uuid                        | uuid                | NO          |                                        |                          |                   |                    |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |  2950
- users       |  c21         | xml                         | xml                 | NO          |                                        |                          |                   |                    |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |   142
- users       |  c22         | ARRAY                       | integer[]           | YES         |                                        |                          |                   |                    |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |  1007
- users       |  c23         | USER-DEFINED                | ltree               | YES         |                                        |                          |                   |                    |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       | 16535
- users       |  c24         | USER-DEFINED                | state               | NO          |                                        |                          |                   |                    |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | e       | 16774
- users       |  c25         | timestamp without time zone | timestamp           | NO          |            now()                       |                          |                   |                  4 |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |  1114
- users       |  c26         | timestamp with time zone    | timestamptz         | NO          |                                        |                          |                   |                  6 |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |  1184
- users       |  c27         | time without time zone      | time                | NO          |                                        |                          |                   |                  6 |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |  1266
- users       |  c28         | int                         | int8                | NO          |                                        |                          |                   |                  6 |               |                     |                    |                | NO          |                |                    |                  |                     |        (c1 + c2)      |         | b       |  1267
- users       |  c29         | interval                    | interval            | NO          |                                        |                          |                   |                  6 |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |  1268
- users       |  c30         | interval                    | interval            | NO          |                                        |                          |                   |                  6 |               |        MONTH        |                    |                | NO          |                |                    |                  |                     |                       |         | b       |  1269
- users       |  c31         | interval                    | interval            | NO          |                                        |                          |                   |                  6 |               | MINUTE TO SECOND(6) |                    |                | NO          |                |                    |                  |                     |                       |         | b       |  1233
- users       |  c32         | bigint                      | int4                | NO          | nextval('public.t1_c32_seq'::regclass) |                          |                32 |                    |             0 |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |    23
- users       |  c33         | USER-DEFINED                | test."status""."    | NO          |  'unknown'::test."status""."           |                          |                   |                    |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | e       | 16775
+ table_name  |  column_name |          data_type          |  formatted          | is_nullable |         column_default                 | character_maximum_length | numeric_precision | datetime_precision | numeric_scale |    interval_type    | character_set_name | collation_name | is_identity | identity_start | identity_increment |   identity_last  | identity_generation | generation_expression | comment | typtype | typelem | elemtyp |  oid
+-------------+--------------+-----------------------------+---------------------|-------------+----------------------------------------+--------------------------+-------------------+--------------------+---------------+---------------------+--------------------+----------------+-------------+----------------+--------------------+------------------+---------------------+-----------------------+---------+---------+---------+---------+-------
+ users       |  id          | bigint                      | int8                | NO          |                                        |                          |                64 |                    |             0 |                     |                    |                | YES         |      100       |          1         |          1       |    BY DEFAULT       |                       |         | b       |         |         |    20
+ users       |  rank        | integer                     | int4                | YES         |                                        |                          |                32 |                    |             0 |                     |                    |                | NO          |                |                    |                  |                     |                       | rank    | b       |         |         |    23
+ users       |  c1          | smallint                    | int2                | NO          |           1000                         |                          |                16 |                    |             0 |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |         |         |    21
+ users       |  c2          | bit                         | bit                 | NO          |                                        |                        1 |                   |                    |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |         |         |  1560
+ users       |  c3          | bit varying                 | varbit              | NO          |                                        |                       10 |                   |                    |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |         |         |  1562
+ users       |  c4          | boolean                     | bool                | NO          |                                        |                          |                   |                    |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |         |         |    16
+ users       |  c5          | bytea                       | bytea               | NO          |                                        |                          |                   |                    |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |         |         |    17
+ users       |  c6          | character                   | bpchar              | NO          |                                        |                      100 |                   |                    |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |         |         |  1042
+ users       |  c7          | character varying           | varchar             | NO          | 'logged_in'::character varying         |                          |                   |                    |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |         |         |  1043
+ users       |  c8          | cidr                        | cidr                | NO          |                                        |                          |                   |                    |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |         |         |   650
+ users       |  c9          | circle                      | circle              | NO          |                                        |                          |                   |                    |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |         |         |   718
+ users       |  c10         | date                        | date                | NO          |                                        |                          |                   |                    |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |         |         |  1082
+ users       |  c11         | time with time zone         | timetz              | NO          |                                        |                          |                   |                    |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |         |         |  1266
+ users       |  c12         | double precision            | float8              | NO          |                                        |                          |                53 |                    |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |         |         |   701
+ users       |  c13         | real                        | float4              | NO          |           random()                     |                          |                24 |                    |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |         |         |   700
+ users       |  c14         | json                        | json                | NO          |           '{}'::json                   |                          |                   |                    |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |         |         |   114
+ users       |  c15         | jsonb                       | jsonb               | NO          |           '{}'::jsonb                  |                          |                   |                    |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |         |         |  3802
+ users       |  c16         | money                       | money               | NO          |                                        |                          |                   |                    |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |         |         |   790
+ users       |  c17         | numeric                     | numeric             | NO          |                                        |                          |                   |                    |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |         |         |  1700
+ users       |  c18         | numeric                     | numeric             | NO          |                                        |                          |                 4 |                    |             4 |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |         |         |  1700
+ users       |  c19         | integer                     | int4                | NO          | nextval('t1_c19_seq'::regclass)        |                          |                32 |                    |             0 |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |         |         |    23
+ users       |  c20         | uuid                        | uuid                | NO          |                                        |                          |                   |                    |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |         |         |  2950
+ users       |  c21         | xml                         | xml                 | NO          |                                        |                          |                   |                    |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |         |         |   142
+ users       |  c22         | ARRAY                       | integer[]           | YES         |                                        |                          |                   |                    |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |         |         |  1007
+ users       |  c23         | USER-DEFINED                | ltree               | YES         |                                        |                          |                   |                    |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |         |         | 16535
+ users       |  c24         | USER-DEFINED                | state               | NO          |                                        |                          |                   |                    |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | e       |         |         | 16774
+ users       |  c25         | timestamp without time zone | timestamp           | NO          |            now()                       |                          |                   |                  4 |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |         |         |  1114
+ users       |  c26         | timestamp with time zone    | timestamptz         | NO          |                                        |                          |                   |                  6 |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |         |         |  1184
+ users       |  c27         | time without time zone      | time                | NO          |                                        |                          |                   |                  6 |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |         |         |  1266
+ users       |  c28         | int                         | int8                | NO          |                                        |                          |                   |                  6 |               |                     |                    |                | NO          |                |                    |                  |                     |        (c1 + c2)      |         | b       |         |         |  1267
+ users       |  c29         | interval                    | interval            | NO          |                                        |                          |                   |                  6 |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |         |         |  1268
+ users       |  c30         | interval                    | interval            | NO          |                                        |                          |                   |                  6 |               |        MONTH        |                    |                | NO          |                |                    |                  |                     |                       |         | b       |         |         |  1269
+ users       |  c31         | interval                    | interval            | NO          |                                        |                          |                   |                  6 |               | MINUTE TO SECOND(6) |                    |                | NO          |                |                    |                  |                     |                       |         | b       |         |         |  1233
+ users       |  c32         | bigint                      | int4                | NO          | nextval('public.t1_c32_seq'::regclass) |                          |                32 |                    |             0 |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |         |         |    23
+ users       |  c33         | USER-DEFINED                | test."status""."    | NO          |  'unknown'::test."status""."           |                          |                   |                    |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | e       |         |         | 16775
+ users       |  c34         | ARRAY                       | state[]             | NO          |                                        |                          |                   |                    |               |                     |                    |                | NO          |                |                    |                  |                     |                       |         | b       |  16774  |  e      | 16779
 `))
 				m.ExpectQuery(sqltest.Escape(`SELECT enumtypid, enumlabel FROM pg_enum WHERE enumtypid IN ($1, $2)`)).
 					WithArgs(16774, 16775).
@@ -132,6 +133,7 @@ func TestDriver_InspectTable(t *testing.T) {
 					{Name: "c31", Type: &schema.ColumnType{Raw: "interval", Type: &IntervalType{T: "interval", F: "MINUTE TO SECOND", Precision: p(6)}}},
 					{Name: "c32", Type: &schema.ColumnType{Raw: "bigserial", Type: &SerialType{T: "bigserial", SequenceName: "t1_c32_seq"}}},
 					{Name: "c33", Type: &schema.ColumnType{Raw: `status".`, Type: &schema.EnumType{T: `status".`, Values: []string{"unknown"}, Schema: schema.New("test")}}, Default: &schema.Literal{V: "'unknown'"}},
+					{Name: "c34", Type: &schema.ColumnType{Raw: "ARRAY", Type: &ArrayType{T: "state[]", Type: &schema.EnumType{T: "state", Values: []string{"on", "off"}, Schema: t.Schema}}}},
 				}, t.Columns)
 			},
 		},
@@ -142,11 +144,11 @@ func TestDriver_InspectTable(t *testing.T) {
 				m.ExpectQuery(queryColumns).
 					WithArgs("public", "users").
 					WillReturnRows(sqltest.Rows(`
-table_name | column_name |      data_type      | formatted |  is_nullable |         column_default          | character_maximum_length | numeric_precision | datetime_precision | numeric_scale | interval_type | character_set_name | collation_name | is_identity | identity_start | identity_increment |   identity_last  | identity_generation | generation_expression | comment | typtype |  oid
------------+-------------+---------------------+-----------+--------------+---------------------------------+--------------------------+-------------------+--------------------+---------------+---------------+--------------------+----------------+-------------+----------------+--------------------+------------------+---------------------+-----------------------+---------+---------+-------
-users      | id          | bigint              | int8      |  NO          |                                 |                          |                64 |                    |             0 |               |                    |                | NO          |                |                    |                  |                     |                       |         | b       |    20
-users      | c1          | smallint            | int2      |  NO          |                                 |                          |                16 |                    |             0 |               |                    |                | NO          |                |                    |                  |                     |                       |         | b       |    21
-users      | parent_id   | bigint              | int8      |  YES         |                                 |                          |                64 |                    |             0 |               |                    |                | NO          |                |                    |                  |                     |                       |         | b       |    22
+table_name | column_name |      data_type      | formatted |  is_nullable |         column_default          | character_maximum_length | numeric_precision | datetime_precision | numeric_scale | interval_type | character_set_name | collation_name | is_identity | identity_start | identity_increment |   identity_last  | identity_generation | generation_expression | comment | typtype | typelem | elemtyp |  oid
+-----------+-------------+---------------------+-----------+--------------+---------------------------------+--------------------------+-------------------+--------------------+---------------+---------------+--------------------+----------------+-------------+----------------+--------------------+------------------+---------------------+-----------------------+---------+---------+---------+---------+-------
+users      | id          | bigint              | int8      |  NO          |                                 |                          |                64 |                    |             0 |               |                    |                | NO          |                |                    |                  |                     |                       |         | b       |         |         |    20
+users      | c1          | smallint            | int2      |  NO          |                                 |                          |                16 |                    |             0 |               |                    |                | NO          |                |                    |                  |                     |                       |         | b       |         |         |    21
+users      | parent_id   | bigint              | int8      |  YES         |                                 |                          |                64 |                    |             0 |               |                    |                | NO          |                |                    |                  |                     |                       |         | b       |         |         |    22
 `))
 				m.ExpectQuery(queryIndexes).
 					WithArgs("public", "users").
@@ -203,11 +205,11 @@ users           | idx6            | brin        | c1          | f       | t
 				m.ExpectQuery(queryColumns).
 					WithArgs("public", "users").
 					WillReturnRows(sqltest.Rows(`
-table_name | column_name |      data_type      | formatted | is_nullable |         column_default          | character_maximum_length | numeric_precision | datetime_precision | numeric_scale | interval_type | character_set_name | collation_name | is_identity | identity_start | identity_increment |   identity_last  | identity_generation | generation_expression | comment | typtype |  oid
------------+-------------+---------------------+-----------+-------------+---------------------------------+--------------------------+-------------------+--------------------+---------------+---------------+--------------------+----------------+-------------+----------------+--------------------+------------------+---------------------+-----------------------+---------+---------+-------
-users      | id          | integer             | int       | NO          |                                 |                          |                32 |                    |             0 |               |                    |                | NO          |                |                    |                  |                     |                       |         | b       |    20
-users      | oid         | integer             | int       | NO          |                                 |                          |                32 |                    |             0 |               |                    |                | NO          |                |                    |                  |                     |                       |         | b       |    21
-users      | uid         | integer             | int       | NO          |                                 |                          |                32 |                    |             0 |               |                    |                | NO          |                |                    |                  |                     |                       |         | b       |    21
+table_name | column_name |      data_type      | formatted | is_nullable |         column_default          | character_maximum_length | numeric_precision | datetime_precision | numeric_scale | interval_type | character_set_name | collation_name | is_identity | identity_start | identity_increment |   identity_last  | identity_generation | generation_expression | comment | typtype | typelem | elemtyp |  oid
+-----------+-------------+---------------------+-----------+-------------+---------------------------------+--------------------------+-------------------+--------------------+---------------+---------------+--------------------+----------------+-------------+----------------+--------------------+------------------+---------------------+-----------------------+---------+---------+---------+---------+-------
+users      | id          | integer             | int       | NO          |                                 |                          |                32 |                    |             0 |               |                    |                | NO          |                |                    |                  |                     |                       |         | b       |         |         |    20
+users      | oid         | integer             | int       | NO          |                                 |                          |                32 |                    |             0 |               |                    |                | NO          |                |                    |                  |                     |                       |         | b       |         |         |    21
+users      | uid         | integer             | int       | NO          |                                 |                          |                32 |                    |             0 |               |                    |                | NO          |                |                    |                  |                     |                       |         | b       |         |         |    21
 `))
 				m.noIndexes()
 				m.ExpectQuery(queryFKs).
@@ -250,11 +252,11 @@ self_reference  | users      | uid         | public       | users
 				m.ExpectQuery(queryColumns).
 					WithArgs("public", "users").
 					WillReturnRows(sqltest.Rows(`
-table_name |column_name | data_type | formatted | is_nullable | column_default | character_maximum_length | numeric_precision | datetime_precision | numeric_scale | interval_type | character_set_name | collation_name | is_identity | identity_start | identity_increment |   identity_last  | identity_generation | generation_expression | comment | typtype | oid
------------+------------+-----------+-----------+-------------+----------------+--------------------------+-------------------+--------------------+---------------+---------------+--------------------+----------------+-------------+----------------+--------------------+------------------+---------------------+-----------------------+---------+---------+-----
-users      | c1         | integer   | int4      | NO          |                |                          |                32 |                    |             0 |               |                    |                | NO          |                |                    |                  |                     |                       |         | b       |  23
-users      | c2         | integer   | int4      | NO          |                |                          |                32 |                    |             0 |               |                    |                | NO          |                |                    |                  |                     |                       |         | b       |  23
-users      | c3         | integer   | int4      | NO          |                |                          |                32 |                    |             0 |               |                    |                | NO          |                |                    |                  |                     |                       |         | b       |  23
+table_name |column_name | data_type | formatted | is_nullable | column_default | character_maximum_length | numeric_precision | datetime_precision | numeric_scale | interval_type | character_set_name | collation_name | is_identity | identity_start | identity_increment |   identity_last  | identity_generation | generation_expression | comment | typtype | typelem | elemtyp | oid
+-----------+------------+-----------+-----------+-------------+----------------+--------------------------+-------------------+--------------------+---------------+---------------+--------------------+----------------+-------------+----------------+--------------------+------------------+---------------------+-----------------------+---------+---------+---------+---------+-----
+users      | c1         | integer   | int4      | NO          |                |                          |                32 |                    |             0 |               |                    |                | NO          |                |                    |                  |                     |                       |         | b       |         |         |  23
+users      | c2         | integer   | int4      | NO          |                |                          |                32 |                    |             0 |               |                    |                | NO          |                |                    |                  |                     |                       |         | b       |         |         |  23
+users      | c3         | integer   | int4      | NO          |                |                          |                32 |                    |             0 |               |                    |                | NO          |                |                    |                  |                     |                       |         | b       |         |         |  23
 `))
 				m.noIndexes()
 				m.noFKs()
@@ -343,13 +345,13 @@ public
 	m.ExpectQuery(sqltest.Escape(fmt.Sprintf(columnsQuery, "$2, $3, $4"))).
 		WithArgs("public", "logs1", "logs2", "logs3").
 		WillReturnRows(sqltest.Rows(`
-table_name |column_name | data_type | formatted | is_nullable | column_default | character_maximum_length | numeric_precision | datetime_precision | numeric_scale | interval_type | character_set_name | collation_name | is_identity | identity_start | identity_increment |   identity_last  | identity_generation | generation_expression | comment | typtype | oid
------------+------------+-----------+-----------+-------------+----------------+--------------------------+-------------------+--------------------+---------------+---------------+--------------------+----------------+-------------+----------------+--------------------+------------------+---------------------+-----------------------+---------+---------+-----
-logs1      | c1         | integer   | integer   | NO          |                |                          |                32 |                    |             0 |               |                    |                | NO          |                |                    |                  |                     |                       |         | b       |  23
-logs2      | c2         | integer   | integer   | NO          |                |                          |                32 |                    |             0 |               |                    |                | NO          |                |                    |                  |                     |                       |         | b       |  23
-logs2      | c3         | integer   | integer   | NO          |                |                          |                32 |                    |             0 |               |                    |                | NO          |                |                    |                  |                     |                       |         | b       |  23
-logs3      | c4         | integer   | integer   | NO          |                |                          |                32 |                    |             0 |               |                    |                | NO          |                |                    |                  |                     |                       |         | b       |  23
-logs3      | c5         | integer   | integer   | NO          |                |                          |                32 |                    |             0 |               |                    |                | NO          |                |                    |                  |                     |                       |         | b       |  23
+table_name |column_name | data_type | formatted | is_nullable | column_default | character_maximum_length | numeric_precision | datetime_precision | numeric_scale | interval_type | character_set_name | collation_name | is_identity | identity_start | identity_increment |   identity_last  | identity_generation | generation_expression | comment | typtype | typelem | elemtyp | oid
+-----------+------------+-----------+-----------+-------------+----------------+--------------------------+-------------------+--------------------+---------------+---------------+--------------------+----------------+-------------+----------------+--------------------+------------------+---------------------+-----------------------+---------+---------+---------+---------+-----
+logs1      | c1         | integer   | integer   | NO          |                |                          |                32 |                    |             0 |               |                    |                | NO          |                |                    |                  |                     |                       |         | b       |         |         |  23
+logs2      | c2         | integer   | integer   | NO          |                |                          |                32 |                    |             0 |               |                    |                | NO          |                |                    |                  |                     |                       |         | b       |         |         |  23
+logs2      | c3         | integer   | integer   | NO          |                |                          |                32 |                    |             0 |               |                    |                | NO          |                |                    |                  |                     |                       |         | b       |         |         |  23
+logs3      | c4         | integer   | integer   | NO          |                |                          |                32 |                    |             0 |               |                    |                | NO          |                |                    |                  |                     |                       |         | b       |         |         |  23
+logs3      | c5         | integer   | integer   | NO          |                |                          |                32 |                    |             0 |               |                    |                | NO          |                |                    |                  |                     |                       |         | b       |         |         |  23
 `))
 	m.ExpectQuery(sqltest.Escape(fmt.Sprintf(indexesQuery, "$2, $3, $4"))).
 		WillReturnRows(sqlmock.NewRows([]string{"table_name", "index_name", "column_name", "primary", "unique", "constraint_type", "predicate", "expression"}))
@@ -411,12 +413,12 @@ public
 	mk.ExpectQuery(queryCrdbColumns).
 		WithArgs("public", "users").
 		WillReturnRows(sqltest.Rows(`
-table_name  | column_name | data_type | formatted | is_nullable |              column_default               | character_maximum_length | numeric_precision | datetime_precision | numeric_scale | interval_type | character_set_name | collation_name | is_identity | identity_start | identity_increment |   identity_last  |  identity_generation  | generation_expression | comment | typtype | oid 
-------------+-------------+-----------+-----------+-------------+-------------------------------------------+--------------------------+-------------------+--------------------+---------------+---------------+--------------------+----------------|-------------+----------------+--------------------+------------------+-----------------------+-----------------------+---------+---------+-----
-users       | a           | bigint    | bigint    | NO          |                                           |                          |                64 |                    |             0 |               |                    |                | NO          |                |                    |                  |                       |                       |         | b       | 20 
-users       | b           | bigint    | bigint    | NO          |                                           |                          |                64 |                    |             0 |               |                    |                | NO          |                |                    |                  |                       |                       |         | b       | 20 
-users       | c           | bigint    | bigint    | NO          |                                           |                          |                64 |                    |             0 |               |                    |                | NO          |                |                    |                  |                       |                       |         | b       | 20 
-users       | d           | bigint    | bigint    | NO          |                                           |                          |                64 |                    |             0 |               |                    |                | NO          |                |                    |                  |                       |                       |         | b       | 20 
+table_name  | column_name | data_type | formatted | is_nullable |              column_default               | character_maximum_length | numeric_precision | datetime_precision | numeric_scale | interval_type | character_set_name | collation_name | is_identity | identity_start | identity_increment |   identity_last  |  identity_generation  | generation_expression | comment | typtype | typelem | elemtyp | oid 
+------------+-------------+-----------+-----------+-------------+-------------------------------------------+--------------------------+-------------------+--------------------+---------------+---------------+--------------------+----------------|-------------+----------------+--------------------+------------------+-----------------------+-----------------------+---------+---------+---------+---------+-----
+users       | a           | bigint    | bigint    | NO          |                                           |                          |                64 |                    |             0 |               |                    |                | NO          |                |                    |                  |                       |                       |         | b       |         |         | 20 
+users       | b           | bigint    | bigint    | NO          |                                           |                          |                64 |                    |             0 |               |                    |                | NO          |                |                    |                  |                       |                       |         | b       |         |         | 20 
+users       | c           | bigint    | bigint    | NO          |                                           |                          |                64 |                    |             0 |               |                    |                | NO          |                |                    |                  |                       |                       |         | b       |         |         | 20 
+users       | d           | bigint    | bigint    | NO          |                                           |                          |                64 |                    |             0 |               |                    |                | NO          |                |                    |                  |                       |                       |         | b       |         |         | 20 
 `))
 	mk.ExpectQuery(queryCrdbIndexes).
 		WithArgs("public", "users").
diff --git a/sql/postgres/migrate_test.go b/sql/postgres/migrate_test.go
index 5c385f76..f975f69f 100644
--- a/sql/postgres/migrate_test.go
+++ b/sql/postgres/migrate_test.go
@@ -188,6 +188,8 @@ func TestPlanChanges(t *testing.T) {
 						Columns: []*schema.Column{
 							{Name: "id", Type: &schema.ColumnType{Type: &schema.IntegerType{T: "integer"}}, Attrs: []schema.Attr{&Identity{}, &schema.Comment{}}},
 							{Name: "text", Type: &schema.ColumnType{Type: &schema.StringType{T: "text"}, Null: true}},
+							{Name: "directions", Type: &schema.ColumnType{Type: &ArrayType{T: "direction[]", Type: &schema.EnumType{T: "direction", Values: []string{"NORTH", "SOUTH"}, Schema: schema.New("public")}}}},
+							{Name: "states", Type: &schema.ColumnType{Type: &ArrayType{T: "state[]", Type: &schema.EnumType{T: "state", Values: []string{"ON", "OFF"}}}}},
 						},
 						Attrs: []schema.Attr{
 							&schema.Comment{},
@@ -199,10 +201,21 @@ func TestPlanChanges(t *testing.T) {
 					},
 				},
 			},
+			mock: func(m mock) {
+				m.ExpectQuery(sqltest.Escape("SELECT * FROM pg_type t JOIN pg_namespace n on t.typnamespace = n.oid WHERE t.typname = $1 AND t.typtype = 'e' AND n.nspname = $2")).
+					WithArgs("direction", "public").
+					WillReturnRows(sqlmock.NewRows([]string{"name"}))
+				m.ExpectQuery(sqltest.Escape("SELECT * FROM pg_type t JOIN pg_namespace n on t.typnamespace = n.oid WHERE t.typname = $1 AND t.typtype = 'e'")).
+					WithArgs("state").
+					WillReturnRows(sqlmock.NewRows([]string{"name"}).AddRow("state"))
+			},
 			wantPlan: &migrate.Plan{
 				Reversible:    true,
 				Transactional: true,
-				Changes:       []*migrate.Change{{Cmd: `CREATE TABLE "posts" ("id" integer NOT NULL GENERATED BY DEFAULT AS IDENTITY, "text" text NULL, CONSTRAINT "id_nonzero" CHECK ("id" > 0), CONSTRAINT "text_len" CHECK (length("text") > 0) NO INHERIT, CONSTRAINT "a_in_b" CHECK ((a) in (b))) PARTITION BY HASH ("text")`, Reverse: `DROP TABLE "posts"`}},
+				Changes: []*migrate.Change{
+					{Cmd: `CREATE TYPE "public"."direction" AS ENUM ('NORTH', 'SOUTH')`, Reverse: `DROP TYPE "public"."direction"`},
+					{Cmd: `CREATE TABLE "posts" ("id" integer NOT NULL GENERATED BY DEFAULT AS IDENTITY, "text" text NULL, "directions" "public"."direction"[] NOT NULL, "states" "state"[] NOT NULL, CONSTRAINT "id_nonzero" CHECK ("id" > 0), CONSTRAINT "text_len" CHECK (length("text") > 0) NO INHERIT, CONSTRAINT "a_in_b" CHECK ((a) in (b))) PARTITION BY HASH ("text")`, Reverse: `DROP TABLE "posts"`},
+				},
 			},
 		},
 		{
diff --git a/sql/postgres/sqlspec_test.go b/sql/postgres/sqlspec_test.go
index f04de979..bb72cdbb 100644
--- a/sql/postgres/sqlspec_test.go
+++ b/sql/postgres/sqlspec_test.go
@@ -694,6 +694,14 @@ func TestMarshalSpec_Enum(t *testing.T) {
 						schema.EnumName("account_type"),
 						schema.EnumValues("private", "business"),
 					),
+					schema.NewColumn("account_states").
+						SetType(&ArrayType{
+							T: "states[]",
+							Type: &schema.EnumType{
+								T:      "state",
+								Values: []string{"on", "off"},
+							},
+						}),
 				),
 			schema.NewTable("table2").
 				AddColumns(
@@ -711,6 +719,10 @@ func TestMarshalSpec_Enum(t *testing.T) {
     null = false
     type = enum.account_type
   }
+  column "account_states" {
+    null = false
+    type = sql("states[]")
+  }
 }
 table "table2" {
   schema = schema.test
@@ -723,6 +735,10 @@ enum "account_type" {
   schema = schema.test
   values = ["private", "business"]
 }
+enum "state" {
+  schema = schema.test
+  values = ["on", "off"]
+}
 schema "test" {
 }
 `