Repository: ariga/atlas
Pull request #704 (2022-04-13 17:29:12)

internal/types: move typeregistry outside sql
need to use the type registry more widely (specifically moving the hcl variables code to the hcl package). still don't want to export it so moving to `internal/types` instead.

reducing some stuttering this way as well


Patch:
diff --git a/cmd/action/migrate.go b/cmd/action/migrate.go
index efd8fbba..429efe8d 100644
--- a/cmd/action/migrate.go
+++ b/cmd/action/migrate.go
@@ -81,7 +81,7 @@ the migration directory state to the desired schema. The desired state can be an
 	// MigrateHashCmd represents the migrate hash command.
 	MigrateHashCmd = &cobra.Command{
 		Use:   "hash",
-		Short: "Hash creates an integrity hash file for the migration directories.",
+		Short: "Hash (re-)creates an integrity hash file for the migration directory.",
 		Long: `'atlas migrate hash' computes the integrity hash sum of the migration directory and stores it in the atlas.sum file.
 This command should be used whenever a manual change in the migration directory was made.`,
 		Example: `  atlas migrate hash --force`,
diff --git a/doc/md/cli/reference.md b/doc/md/cli/reference.md
index be5e60b8..91a03e78 100644
--- a/doc/md/cli/reference.md
+++ b/doc/md/cli/reference.md
@@ -98,7 +98,7 @@ the migration directory state to the desired schema. The desired state can be an
 
 ### atlas migrate hash
 
-Hash creates an integrity hash file for the migration directories.
+Hash (re-)creates an integrity hash file for the migration directory.
 
 #### Usage
 ```
diff --git a/schema/schemaspec/internal/schemautil/schemautil.go b/schema/schemaspec/internal/schemautil/schemautil.go
index bfdc0827..cae78fde 100644
--- a/schema/schemaspec/internal/schemautil/schemautil.go
+++ b/schema/schemaspec/internal/schemautil/schemautil.go
@@ -21,13 +21,13 @@ func StrLitAttr(k, v string) *schemaspec.Attr {
 }
 
 // ListAttr is a helper method for constructing *schemaspec.Attr instances that contain list values.
-func ListAttr(k string, values ...string) *schemaspec.Attr {
-	lst := &schemaspec.ListValue{}
-	for _, v := range values {
-		lst.V = append(lst.V, &schemaspec.LiteralValue{V: strconv.Quote(v)})
+func ListAttr(k string, litValues ...string) *schemaspec.Attr {
+	lv := &schemaspec.ListValue{}
+	for _, v := range litValues {
+		lv.V = append(lv.V, &schemaspec.LiteralValue{V: v})
 	}
 	return &schemaspec.Attr{
 		K: k,
-		V: lst,
+		V: lv,
 	}
 }
diff --git a/sql/internal/specutil/types.go b/schema/schemaspec/schemahcl/types.go
similarity index 92%
rename from sql/internal/specutil/types.go
rename to schema/schemaspec/schemahcl/types.go
index 7d3962d1..d5f3d224 100644
--- a/sql/internal/specutil/types.go
+++ b/schema/schemaspec/schemahcl/types.go
@@ -1,4 +1,4 @@
-package specutil
+package schemahcl
 
 import (
 	"errors"
@@ -9,6 +9,7 @@ import (
 	"strings"
 
 	"ariga.io/atlas/schema/schemaspec"
+	"ariga.io/atlas/schema/schemaspec/internal/schemautil"
 	"ariga.io/atlas/sql/schema"
 
 	"github.com/go-openapi/inflect"
@@ -74,7 +75,7 @@ type TypeRegistry struct {
 
 // WithFormatter configures the registry to use a formatting function for printing
 // schema.Type as string.
-func WithFormatter(f func(schema.Type) (string, error)) RegistryOption {
+func WithFormatter(f func(schema.Type) (string, error)) TypeRegistryOption {
 	return func(registry *TypeRegistry) error {
 		registry.spec = func(t schema.Type) (*schemaspec.Type, error) {
 			s, err := f(t)
@@ -89,7 +90,7 @@ func WithFormatter(f func(schema.Type) (string, error)) RegistryOption {
 
 // WithSpecFunc configures the registry to use the given function for converting
 // a schema.Type to schemaspec.Type
-func WithSpecFunc(spec func(schema.Type) (*schemaspec.Type, error)) RegistryOption {
+func WithSpecFunc(spec func(schema.Type) (*schemaspec.Type, error)) TypeRegistryOption {
 	return func(registry *TypeRegistry) error {
 		registry.spec = spec
 		return nil
@@ -98,7 +99,7 @@ func WithSpecFunc(spec func(schema.Type) (*schemaspec.Type, error)) RegistryOpti
 
 // WithParser configures the registry to use a parsing function for converting
 // a string to a schema.Type.
-func WithParser(parser func(string) (schema.Type, error)) RegistryOption {
+func WithParser(parser func(string) (schema.Type, error)) TypeRegistryOption {
 	return func(registry *TypeRegistry) error {
 		registry.parser = parser
 		return nil
@@ -136,11 +137,11 @@ func validSpec(typeSpec *schemaspec.TypeSpec) error {
 	return nil
 }
 
-// RegistryOption configures a TypeRegistry.
-type RegistryOption func(*TypeRegistry) error
+// TypeRegistryOption configures a TypeRegistry.
+type TypeRegistryOption func(*TypeRegistry) error
 
 // WithSpecs configures the registry to register the given list of type specs.
-func WithSpecs(specs ...*schemaspec.TypeSpec) RegistryOption {
+func WithSpecs(specs ...*schemaspec.TypeSpec) TypeRegistryOption {
 	return func(registry *TypeRegistry) error {
 		if err := registry.Register(specs...); err != nil {
 			return fmt.Errorf("failed registering types: %s", err)
@@ -151,7 +152,7 @@ func WithSpecs(specs ...*schemaspec.TypeSpec) RegistryOption {
 
 // NewRegistry creates a new *TypeRegistry, registers the provided types and panics
 // if an error occurs.
-func NewRegistry(opts ...RegistryOption) *TypeRegistry {
+func NewRegistry(opts ...TypeRegistryOption) *TypeRegistry {
 	r := &TypeRegistry{}
 	for _, opt := range opts {
 		if err := opt(r); err != nil {
@@ -219,14 +220,14 @@ func (r *TypeRegistry) Convert(typ schema.Type) (*schemaspec.Type, error) {
 				break
 			}
 			i := strconv.Itoa(v)
-			s.Attrs = append([]*schemaspec.Attr{LitAttr(attr.Name, i)}, s.Attrs...)
+			s.Attrs = append([]*schemaspec.Attr{schemautil.LitAttr(attr.Name, i)}, s.Attrs...)
 		case reflect.Bool:
 			v := field.Bool()
 			if !v && len(s.Attrs) == 0 {
 				break
 			}
 			b := strconv.FormatBool(v)
-			s.Attrs = append([]*schemaspec.Attr{LitAttr(attr.Name, b)}, s.Attrs...)
+			s.Attrs = append([]*schemaspec.Attr{schemautil.LitAttr(attr.Name, b)}, s.Attrs...)
 		case reflect.Slice:
 			lits := make([]string, 0, field.Len())
 			for i := 0; i < field.Len(); i++ {
@@ -236,7 +237,7 @@ func (r *TypeRegistry) Convert(typ schema.Type) (*schemaspec.Type, error) {
 				}
 				lits = append(lits, strconv.Quote(fi.String()))
 			}
-			s.Attrs = append([]*schemaspec.Attr{ListAttr(attr.Name, lits...)}, s.Attrs...)
+			s.Attrs = append([]*schemaspec.Attr{schemautil.ListAttr(attr.Name, lits...)}, s.Attrs...)
 		default:
 			return nil, fmt.Errorf("specutil: unsupported attr kind %s for attribute %q of %q", attr.Kind, attr.Name, typeSpec.Name)
 		}
diff --git a/sql/internal/specutil/vars.go b/sql/internal/specutil/vars.go
index 557991f8..30ea03c5 100644
--- a/sql/internal/specutil/vars.go
+++ b/sql/internal/specutil/vars.go
@@ -29,11 +29,11 @@ func ExtractVarsHCL(body []byte) ([]*InputVar, error) {
 
 var (
 	hclState = schemahcl.New(schemahcl.WithTypes(
-		NewRegistry(
-			WithSpecs(
-				TypeSpec("int"),
-				TypeSpec("bool"),
-				TypeSpec("float"),
+		schemahcl.NewRegistry(
+			schemahcl.WithSpecs(
+				schemahcl.TypeSpec("int"),
+				schemahcl.TypeSpec("bool"),
+				schemahcl.TypeSpec("float"),
 			),
 		).Specs(),
 	))
diff --git a/sql/migrate/migrate.go b/sql/migrate/migrate.go
index 1a622819..d837260a 100644
--- a/sql/migrate/migrate.go
+++ b/sql/migrate/migrate.go
@@ -360,13 +360,8 @@ type LocalDir struct {
 // NewLocalDir returns a new the Dir used by a Planner to work on the given local path.
 func NewLocalDir(path string) (*LocalDir, error) {
 	fi, err := os.Stat(path)
-	if os.IsNotExist(err) {
-		if err := os.MkdirAll(path, 0755); err != nil {
-			return nil, err
-		}
-		return &LocalDir{dir: path}, nil
-	} else if err != nil {
-		return nil, err
+	if err != nil {
+		return nil, fmt.Errorf("sql/migrate: %w", err)
 	}
 	if !fi.IsDir() {
 		return nil, fmt.Errorf("sql/migrate: %q is not a dir", path)
diff --git a/sql/mysql/sqlspec.go b/sql/mysql/sqlspec.go
index 2710b4cc..8f69ed24 100644
--- a/sql/mysql/sqlspec.go
+++ b/sql/mysql/sqlspec.go
@@ -388,10 +388,10 @@ func hasCollate(attr []schema.Attr, parent []schema.Attr) (string, bool) {
 }
 
 // TypeRegistry contains the supported TypeSpecs for the mysql driver.
-var TypeRegistry = specutil.NewRegistry(
-	specutil.WithFormatter(FormatType),
-	specutil.WithParser(ParseType),
-	specutil.WithSpecs(
+var TypeRegistry = schemahcl.NewRegistry(
+	schemahcl.WithFormatter(FormatType),
+	schemahcl.WithParser(ParseType),
+	schemahcl.WithSpecs(
 		&schemaspec.TypeSpec{
 			Name: TypeEnum,
 			T:    TypeEnum,
@@ -408,45 +408,45 @@ var TypeRegistry = specutil.NewRegistry(
 			},
 			RType: reflect.TypeOf(SetType{}),
 		},
-		specutil.TypeSpec(TypeBool),
-		specutil.TypeSpec(TypeBoolean),
-		specutil.TypeSpec(TypeBit, specutil.WithAttributes(specutil.SizeTypeAttr(false))),
-		specutil.TypeSpec(TypeInt, specutil.WithAttributes(unsignedTypeAttr(), specutil.SizeTypeAttr(false))),
-		specutil.TypeSpec(TypeTinyInt, specutil.WithAttributes(unsignedTypeAttr(), specutil.SizeTypeAttr(false))),
-		specutil.TypeSpec(TypeSmallInt, specutil.WithAttributes(unsignedTypeAttr(), specutil.SizeTypeAttr(false))),
-		specutil.TypeSpec(TypeMediumInt, specutil.WithAttributes(unsignedTypeAttr(), specutil.SizeTypeAttr(false))),
-		specutil.TypeSpec(TypeBigInt, specutil.WithAttributes(unsignedTypeAttr(), specutil.SizeTypeAttr(false))),
-		specutil.TypeSpec(TypeDecimal, specutil.WithAttributes(unsignedTypeAttr(), &schemaspec.TypeAttr{Name: "precision", Kind: reflect.Int, Required: false}, &schemaspec.TypeAttr{Name: "scale", Kind: reflect.Int, Required: false})),
-		specutil.TypeSpec(TypeNumeric, specutil.WithAttributes(unsignedTypeAttr(), &schemaspec.TypeAttr{Name: "precision", Kind: reflect.Int, Required: false}, &schemaspec.TypeAttr{Name: "scale", Kind: reflect.Int, Required: false})),
-		specutil.TypeSpec(TypeFloat, specutil.WithAttributes(unsignedTypeAttr(), &schemaspec.TypeAttr{Name: "precision", Kind: reflect.Int, Required: false}, &schemaspec.TypeAttr{Name: "scale", Kind: reflect.Int, Required: false})),
-		specutil.TypeSpec(TypeDouble, specutil.WithAttributes(unsignedTypeAttr(), &schemaspec.TypeAttr{Name: "precision", Kind: reflect.Int, Required: false}, &schemaspec.TypeAttr{Name: "scale", Kind: reflect.Int, Required: false})),
-		specutil.TypeSpec(TypeReal, specutil.WithAttributes(unsignedTypeAttr(), &schemaspec.TypeAttr{Name: "precision", Kind: reflect.Int, Required: false}, &schemaspec.TypeAttr{Name: "scale", Kind: reflect.Int, Required: false})),
-		specutil.TypeSpec(TypeTimestamp, specutil.WithAttributes(&schemaspec.TypeAttr{Name: "precision", Kind: reflect.Int, Required: false})),
-		specutil.TypeSpec(TypeDate, specutil.WithAttributes(&schemaspec.TypeAttr{Name: "precision", Kind: reflect.Int, Required: false})),
-		specutil.TypeSpec(TypeTime, specutil.WithAttributes(&schemaspec.TypeAttr{Name: "precision", Kind: reflect.Int, Required: false})),
-		specutil.TypeSpec(TypeDateTime, specutil.WithAttributes(&schemaspec.TypeAttr{Name: "precision", Kind: reflect.Int, Required: false})),
-		specutil.TypeSpec(TypeYear, specutil.WithAttributes(&schemaspec.TypeAttr{Name: "precision", Kind: reflect.Int, Required: false})),
-		specutil.TypeSpec(TypeVarchar, specutil.WithAttributes(specutil.SizeTypeAttr(true))),
-		specutil.TypeSpec(TypeChar, specutil.WithAttributes(specutil.SizeTypeAttr(false))),
-		specutil.TypeSpec(TypeVarBinary, specutil.WithAttributes(specutil.SizeTypeAttr(false))),
-		specutil.TypeSpec(TypeBinary, specutil.WithAttributes(specutil.SizeTypeAttr(false))),
-		specutil.TypeSpec(TypeBlob, specutil.WithAttributes(specutil.SizeTypeAttr(false))),
-		specutil.TypeSpec(TypeTinyBlob),
-		specutil.TypeSpec(TypeMediumBlob),
-		specutil.TypeSpec(TypeLongBlob),
-		specutil.TypeSpec(TypeJSON),
-		specutil.TypeSpec(TypeText, specutil.WithAttributes(specutil.SizeTypeAttr(false))),
-		specutil.TypeSpec(TypeTinyText),
-		specutil.TypeSpec(TypeMediumText),
-		specutil.TypeSpec(TypeLongText),
-		specutil.TypeSpec(TypeGeometry),
-		specutil.TypeSpec(TypePoint),
-		specutil.TypeSpec(TypeMultiPoint),
-		specutil.TypeSpec(TypeLineString),
-		specutil.TypeSpec(TypeMultiLineString),
-		specutil.TypeSpec(TypePolygon),
-		specutil.TypeSpec(TypeMultiPolygon),
-		specutil.TypeSpec(TypeGeometryCollection),
+		schemahcl.TypeSpec(TypeBool),
+		schemahcl.TypeSpec(TypeBoolean),
+		schemahcl.TypeSpec(TypeBit, schemahcl.WithAttributes(schemahcl.SizeTypeAttr(false))),
+		schemahcl.TypeSpec(TypeInt, schemahcl.WithAttributes(unsignedTypeAttr(), schemahcl.SizeTypeAttr(false))),
+		schemahcl.TypeSpec(TypeTinyInt, schemahcl.WithAttributes(unsignedTypeAttr(), schemahcl.SizeTypeAttr(false))),
+		schemahcl.TypeSpec(TypeSmallInt, schemahcl.WithAttributes(unsignedTypeAttr(), schemahcl.SizeTypeAttr(false))),
+		schemahcl.TypeSpec(TypeMediumInt, schemahcl.WithAttributes(unsignedTypeAttr(), schemahcl.SizeTypeAttr(false))),
+		schemahcl.TypeSpec(TypeBigInt, schemahcl.WithAttributes(unsignedTypeAttr(), schemahcl.SizeTypeAttr(false))),
+		schemahcl.TypeSpec(TypeDecimal, schemahcl.WithAttributes(unsignedTypeAttr(), &schemaspec.TypeAttr{Name: "precision", Kind: reflect.Int, Required: false}, &schemaspec.TypeAttr{Name: "scale", Kind: reflect.Int, Required: false})),
+		schemahcl.TypeSpec(TypeNumeric, schemahcl.WithAttributes(unsignedTypeAttr(), &schemaspec.TypeAttr{Name: "precision", Kind: reflect.Int, Required: false}, &schemaspec.TypeAttr{Name: "scale", Kind: reflect.Int, Required: false})),
+		schemahcl.TypeSpec(TypeFloat, schemahcl.WithAttributes(unsignedTypeAttr(), &schemaspec.TypeAttr{Name: "precision", Kind: reflect.Int, Required: false}, &schemaspec.TypeAttr{Name: "scale", Kind: reflect.Int, Required: false})),
+		schemahcl.TypeSpec(TypeDouble, schemahcl.WithAttributes(unsignedTypeAttr(), &schemaspec.TypeAttr{Name: "precision", Kind: reflect.Int, Required: false}, &schemaspec.TypeAttr{Name: "scale", Kind: reflect.Int, Required: false})),
+		schemahcl.TypeSpec(TypeReal, schemahcl.WithAttributes(unsignedTypeAttr(), &schemaspec.TypeAttr{Name: "precision", Kind: reflect.Int, Required: false}, &schemaspec.TypeAttr{Name: "scale", Kind: reflect.Int, Required: false})),
+		schemahcl.TypeSpec(TypeTimestamp, schemahcl.WithAttributes(&schemaspec.TypeAttr{Name: "precision", Kind: reflect.Int, Required: false})),
+		schemahcl.TypeSpec(TypeDate, schemahcl.WithAttributes(&schemaspec.TypeAttr{Name: "precision", Kind: reflect.Int, Required: false})),
+		schemahcl.TypeSpec(TypeTime, schemahcl.WithAttributes(&schemaspec.TypeAttr{Name: "precision", Kind: reflect.Int, Required: false})),
+		schemahcl.TypeSpec(TypeDateTime, schemahcl.WithAttributes(&schemaspec.TypeAttr{Name: "precision", Kind: reflect.Int, Required: false})),
+		schemahcl.TypeSpec(TypeYear, schemahcl.WithAttributes(&schemaspec.TypeAttr{Name: "precision", Kind: reflect.Int, Required: false})),
+		schemahcl.TypeSpec(TypeVarchar, schemahcl.WithAttributes(schemahcl.SizeTypeAttr(true))),
+		schemahcl.TypeSpec(TypeChar, schemahcl.WithAttributes(schemahcl.SizeTypeAttr(false))),
+		schemahcl.TypeSpec(TypeVarBinary, schemahcl.WithAttributes(schemahcl.SizeTypeAttr(false))),
+		schemahcl.TypeSpec(TypeBinary, schemahcl.WithAttributes(schemahcl.SizeTypeAttr(false))),
+		schemahcl.TypeSpec(TypeBlob, schemahcl.WithAttributes(schemahcl.SizeTypeAttr(false))),
+		schemahcl.TypeSpec(TypeTinyBlob),
+		schemahcl.TypeSpec(TypeMediumBlob),
+		schemahcl.TypeSpec(TypeLongBlob),
+		schemahcl.TypeSpec(TypeJSON),
+		schemahcl.TypeSpec(TypeText, schemahcl.WithAttributes(schemahcl.SizeTypeAttr(false))),
+		schemahcl.TypeSpec(TypeTinyText),
+		schemahcl.TypeSpec(TypeMediumText),
+		schemahcl.TypeSpec(TypeLongText),
+		schemahcl.TypeSpec(TypeGeometry),
+		schemahcl.TypeSpec(TypePoint),
+		schemahcl.TypeSpec(TypeMultiPoint),
+		schemahcl.TypeSpec(TypeLineString),
+		schemahcl.TypeSpec(TypeMultiLineString),
+		schemahcl.TypeSpec(TypePolygon),
+		schemahcl.TypeSpec(TypeMultiPolygon),
+		schemahcl.TypeSpec(TypeGeometryCollection),
 	),
 )
 
diff --git a/sql/postgres/sqlspec.go b/sql/postgres/sqlspec.go
index 4b8baaf0..2b75d09a 100644
--- a/sql/postgres/sqlspec.go
+++ b/sql/postgres/sqlspec.go
@@ -379,61 +379,61 @@ func columnTypeSpec(t schema.Type) (*sqlspec.Column, error) {
 }
 
 // TypeRegistry contains the supported TypeSpecs for the Postgres driver.
-var TypeRegistry = specutil.NewRegistry(
-	specutil.WithSpecFunc(typeSpec),
-	specutil.WithParser(ParseType),
-	specutil.WithSpecs(
-		specutil.TypeSpec(TypeBit, specutil.WithAttributes(&schemaspec.TypeAttr{Name: "len", Kind: reflect.Int64})),
-		specutil.AliasTypeSpec("bit_varying", TypeBitVar, specutil.WithAttributes(&schemaspec.TypeAttr{Name: "len", Kind: reflect.Int64})),
-		specutil.TypeSpec(TypeVarChar, specutil.WithAttributes(specutil.SizeTypeAttr(false))),
-		specutil.AliasTypeSpec("character_varying", TypeCharVar, specutil.WithAttributes(specutil.SizeTypeAttr(false))),
-		specutil.TypeSpec(TypeChar, specutil.WithAttributes(specutil.SizeTypeAttr(true))),
-		specutil.TypeSpec(TypeCharacter, specutil.WithAttributes(specutil.SizeTypeAttr(true))),
-		specutil.TypeSpec(TypeInt2),
-		specutil.TypeSpec(TypeInt4),
-		specutil.TypeSpec(TypeInt8),
-		specutil.TypeSpec(TypeInt),
-		specutil.TypeSpec(TypeInteger),
-		specutil.TypeSpec(TypeSmallInt),
-		specutil.TypeSpec(TypeBigInt),
-		specutil.TypeSpec(TypeText),
-		specutil.TypeSpec(TypeBoolean),
-		specutil.TypeSpec(TypeBool),
-		specutil.TypeSpec(TypeBytea),
-		specutil.TypeSpec(TypeCIDR),
-		specutil.TypeSpec(TypeInet),
-		specutil.TypeSpec(TypeMACAddr),
-		specutil.TypeSpec(TypeMACAddr8),
-		specutil.TypeSpec(TypeCircle),
-		specutil.TypeSpec(TypeLine),
-		specutil.TypeSpec(TypeLseg),
-		specutil.TypeSpec(TypeBox),
-		specutil.TypeSpec(TypePath),
-		specutil.TypeSpec(TypePoint),
-		specutil.TypeSpec(TypeDate),
-		specutil.TypeSpec(TypeTime, specutil.WithAttributes(precisionTypeAttr()), formatTime()),
-		specutil.TypeSpec(TypeTimeTZ, specutil.WithAttributes(precisionTypeAttr()), formatTime()),
-		specutil.TypeSpec(TypeTimestampTZ, specutil.WithAttributes(precisionTypeAttr()), formatTime()),
-		specutil.TypeSpec(TypeTimestamp, specutil.WithAttributes(precisionTypeAttr()), formatTime()),
-		specutil.AliasTypeSpec("double_precision", TypeDouble),
-		specutil.TypeSpec(TypeReal),
-		specutil.TypeSpec(TypeFloat8),
-		specutil.TypeSpec(TypeFloat4),
-		specutil.TypeSpec(TypeNumeric),
-		specutil.TypeSpec(TypeDecimal),
-		specutil.TypeSpec(TypeSmallSerial),
-		specutil.TypeSpec(TypeSerial),
-		specutil.TypeSpec(TypeBigSerial),
-		specutil.TypeSpec(TypeSerial2),
-		specutil.TypeSpec(TypeSerial4),
-		specutil.TypeSpec(TypeSerial8),
-		specutil.TypeSpec(TypeXML),
-		specutil.TypeSpec(TypeJSON),
-		specutil.TypeSpec(TypeJSONB),
-		specutil.TypeSpec(TypeUUID),
-		specutil.TypeSpec(TypeMoney),
-		specutil.TypeSpec("hstore"),
-		specutil.TypeSpec("sql", specutil.WithAttributes(&schemaspec.TypeAttr{Name: "def", Required: true, Kind: reflect.String})),
+var TypeRegistry = schemahcl.NewRegistry(
+	schemahcl.WithSpecFunc(typeSpec),
+	schemahcl.WithParser(ParseType),
+	schemahcl.WithSpecs(
+		schemahcl.TypeSpec(TypeBit, schemahcl.WithAttributes(&schemaspec.TypeAttr{Name: "len", Kind: reflect.Int64})),
+		schemahcl.AliasTypeSpec("bit_varying", TypeBitVar, schemahcl.WithAttributes(&schemaspec.TypeAttr{Name: "len", Kind: reflect.Int64})),
+		schemahcl.TypeSpec(TypeVarChar, schemahcl.WithAttributes(schemahcl.SizeTypeAttr(false))),
+		schemahcl.AliasTypeSpec("character_varying", TypeCharVar, schemahcl.WithAttributes(schemahcl.SizeTypeAttr(false))),
+		schemahcl.TypeSpec(TypeChar, schemahcl.WithAttributes(schemahcl.SizeTypeAttr(true))),
+		schemahcl.TypeSpec(TypeCharacter, schemahcl.WithAttributes(schemahcl.SizeTypeAttr(true))),
+		schemahcl.TypeSpec(TypeInt2),
+		schemahcl.TypeSpec(TypeInt4),
+		schemahcl.TypeSpec(TypeInt8),
+		schemahcl.TypeSpec(TypeInt),
+		schemahcl.TypeSpec(TypeInteger),
+		schemahcl.TypeSpec(TypeSmallInt),
+		schemahcl.TypeSpec(TypeBigInt),
+		schemahcl.TypeSpec(TypeText),
+		schemahcl.TypeSpec(TypeBoolean),
+		schemahcl.TypeSpec(TypeBool),
+		schemahcl.TypeSpec(TypeBytea),
+		schemahcl.TypeSpec(TypeCIDR),
+		schemahcl.TypeSpec(TypeInet),
+		schemahcl.TypeSpec(TypeMACAddr),
+		schemahcl.TypeSpec(TypeMACAddr8),
+		schemahcl.TypeSpec(TypeCircle),
+		schemahcl.TypeSpec(TypeLine),
+		schemahcl.TypeSpec(TypeLseg),
+		schemahcl.TypeSpec(TypeBox),
+		schemahcl.TypeSpec(TypePath),
+		schemahcl.TypeSpec(TypePoint),
+		schemahcl.TypeSpec(TypeDate),
+		schemahcl.TypeSpec(TypeTime, schemahcl.WithAttributes(precisionTypeAttr()), formatTime()),
+		schemahcl.TypeSpec(TypeTimeTZ, schemahcl.WithAttributes(precisionTypeAttr()), formatTime()),
+		schemahcl.TypeSpec(TypeTimestampTZ, schemahcl.WithAttributes(precisionTypeAttr()), formatTime()),
+		schemahcl.TypeSpec(TypeTimestamp, schemahcl.WithAttributes(precisionTypeAttr()), formatTime()),
+		schemahcl.AliasTypeSpec("double_precision", TypeDouble),
+		schemahcl.TypeSpec(TypeReal),
+		schemahcl.TypeSpec(TypeFloat8),
+		schemahcl.TypeSpec(TypeFloat4),
+		schemahcl.TypeSpec(TypeNumeric),
+		schemahcl.TypeSpec(TypeDecimal),
+		schemahcl.TypeSpec(TypeSmallSerial),
+		schemahcl.TypeSpec(TypeSerial),
+		schemahcl.TypeSpec(TypeBigSerial),
+		schemahcl.TypeSpec(TypeSerial2),
+		schemahcl.TypeSpec(TypeSerial4),
+		schemahcl.TypeSpec(TypeSerial8),
+		schemahcl.TypeSpec(TypeXML),
+		schemahcl.TypeSpec(TypeJSON),
+		schemahcl.TypeSpec(TypeJSONB),
+		schemahcl.TypeSpec(TypeUUID),
+		schemahcl.TypeSpec(TypeMoney),
+		schemahcl.TypeSpec("hstore"),
+		schemahcl.TypeSpec("sql", schemahcl.WithAttributes(&schemaspec.TypeAttr{Name: "def", Required: true, Kind: reflect.String})),
 	),
 )
 
@@ -470,8 +470,8 @@ func typeSpec(t schema.Type) (*schemaspec.Type, error) {
 }
 
 // formatTime overrides the default printing logic done by schemahcl.hclType.
-func formatTime() specutil.TypeSpecOption {
-	return specutil.WithTypeFormatter(func(t *schemaspec.Type) (string, error) {
+func formatTime() schemahcl.TypeSpecOption {
+	return schemahcl.WithTypeFormatter(func(t *schemaspec.Type) (string, error) {
 		a, ok := attr(t, "precision")
 		if !ok {
 			return t.T, nil
diff --git a/sql/sqlite/sqlspec.go b/sql/sqlite/sqlspec.go
index 139f86c4..42c5be56 100644
--- a/sql/sqlite/sqlspec.go
+++ b/sql/sqlite/sqlspec.go
@@ -118,39 +118,39 @@ func columnTypeSpec(t schema.Type) (*sqlspec.Column, error) {
 }
 
 // TypeRegistry contains the supported TypeSpecs for the sqlite driver.
-var TypeRegistry = specutil.NewRegistry(
-	specutil.WithFormatter(FormatType),
-	specutil.WithParser(ParseType),
-	specutil.WithSpecs(
-		specutil.TypeSpec(TypeReal, specutil.WithAttributes(&schemaspec.TypeAttr{Name: "precision", Kind: reflect.Int, Required: false}, &schemaspec.TypeAttr{Name: "scale", Kind: reflect.Int, Required: false})),
-		specutil.TypeSpec(TypeBlob, specutil.WithAttributes(specutil.SizeTypeAttr(false))),
-		specutil.TypeSpec(TypeText, specutil.WithAttributes(specutil.SizeTypeAttr(false))),
-		specutil.TypeSpec(TypeInteger, specutil.WithAttributes(specutil.SizeTypeAttr(false))),
-		specutil.TypeSpec("int", specutil.WithAttributes(specutil.SizeTypeAttr(false))),
-		specutil.TypeSpec("tinyint", specutil.WithAttributes(specutil.SizeTypeAttr(false))),
-		specutil.TypeSpec("smallint", specutil.WithAttributes(specutil.SizeTypeAttr(false))),
-		specutil.TypeSpec("mediumint", specutil.WithAttributes(specutil.SizeTypeAttr(false))),
-		specutil.TypeSpec("bigint", specutil.WithAttributes(specutil.SizeTypeAttr(false))),
-		specutil.AliasTypeSpec("unsigned_big_int", "unsigned big int", specutil.WithAttributes(specutil.SizeTypeAttr(false))),
-		specutil.TypeSpec("int2", specutil.WithAttributes(specutil.SizeTypeAttr(false))),
-		specutil.TypeSpec("int8", specutil.WithAttributes(specutil.SizeTypeAttr(false))),
-		specutil.TypeSpec("double", specutil.WithAttributes(specutil.SizeTypeAttr(false))),
-		specutil.AliasTypeSpec("double_precision", "double precision", specutil.WithAttributes(specutil.SizeTypeAttr(false))),
-		specutil.TypeSpec("float", specutil.WithAttributes(specutil.SizeTypeAttr(false))),
-		specutil.TypeSpec("character", specutil.WithAttributes(specutil.SizeTypeAttr(false))),
-		specutil.TypeSpec("varchar", specutil.WithAttributes(specutil.SizeTypeAttr(false))),
-		specutil.AliasTypeSpec("varying_character", "varying character", specutil.WithAttributes(specutil.SizeTypeAttr(false))),
-		specutil.TypeSpec("nchar", specutil.WithAttributes(specutil.SizeTypeAttr(false))),
-		specutil.AliasTypeSpec("native_character", "native character", specutil.WithAttributes(specutil.SizeTypeAttr(false))),
-		specutil.TypeSpec("nvarchar", specutil.WithAttributes(specutil.SizeTypeAttr(false))),
-		specutil.TypeSpec("clob", specutil.WithAttributes(specutil.SizeTypeAttr(false))),
-		specutil.TypeSpec("numeric", specutil.WithAttributes(&schemaspec.TypeAttr{Name: "precision", Kind: reflect.Int, Required: false}, &schemaspec.TypeAttr{Name: "scale", Kind: reflect.Int, Required: false})),
-		specutil.TypeSpec("decimal", specutil.WithAttributes(&schemaspec.TypeAttr{Name: "precision", Kind: reflect.Int, Required: false}, &schemaspec.TypeAttr{Name: "scale", Kind: reflect.Int, Required: false})),
-		specutil.TypeSpec("boolean"),
-		specutil.TypeSpec("date"),
-		specutil.TypeSpec("datetime"),
-		specutil.TypeSpec("json"),
-		specutil.TypeSpec("uuid"),
+var TypeRegistry = schemahcl.NewRegistry(
+	schemahcl.WithFormatter(FormatType),
+	schemahcl.WithParser(ParseType),
+	schemahcl.WithSpecs(
+		schemahcl.TypeSpec(TypeReal, schemahcl.WithAttributes(&schemaspec.TypeAttr{Name: "precision", Kind: reflect.Int, Required: false}, &schemaspec.TypeAttr{Name: "scale", Kind: reflect.Int, Required: false})),
+		schemahcl.TypeSpec(TypeBlob, schemahcl.WithAttributes(schemahcl.SizeTypeAttr(false))),
+		schemahcl.TypeSpec(TypeText, schemahcl.WithAttributes(schemahcl.SizeTypeAttr(false))),
+		schemahcl.TypeSpec(TypeInteger, schemahcl.WithAttributes(schemahcl.SizeTypeAttr(false))),
+		schemahcl.TypeSpec("int", schemahcl.WithAttributes(schemahcl.SizeTypeAttr(false))),
+		schemahcl.TypeSpec("tinyint", schemahcl.WithAttributes(schemahcl.SizeTypeAttr(false))),
+		schemahcl.TypeSpec("smallint", schemahcl.WithAttributes(schemahcl.SizeTypeAttr(false))),
+		schemahcl.TypeSpec("mediumint", schemahcl.WithAttributes(schemahcl.SizeTypeAttr(false))),
+		schemahcl.TypeSpec("bigint", schemahcl.WithAttributes(schemahcl.SizeTypeAttr(false))),
+		schemahcl.AliasTypeSpec("unsigned_big_int", "unsigned big int", schemahcl.WithAttributes(schemahcl.SizeTypeAttr(false))),
+		schemahcl.TypeSpec("int2", schemahcl.WithAttributes(schemahcl.SizeTypeAttr(false))),
+		schemahcl.TypeSpec("int8", schemahcl.WithAttributes(schemahcl.SizeTypeAttr(false))),
+		schemahcl.TypeSpec("double", schemahcl.WithAttributes(schemahcl.SizeTypeAttr(false))),
+		schemahcl.AliasTypeSpec("double_precision", "double precision", schemahcl.WithAttributes(schemahcl.SizeTypeAttr(false))),
+		schemahcl.TypeSpec("float", schemahcl.WithAttributes(schemahcl.SizeTypeAttr(false))),
+		schemahcl.TypeSpec("character", schemahcl.WithAttributes(schemahcl.SizeTypeAttr(false))),
+		schemahcl.TypeSpec("varchar", schemahcl.WithAttributes(schemahcl.SizeTypeAttr(false))),
+		schemahcl.AliasTypeSpec("varying_character", "varying character", schemahcl.WithAttributes(schemahcl.SizeTypeAttr(false))),
+		schemahcl.TypeSpec("nchar", schemahcl.WithAttributes(schemahcl.SizeTypeAttr(false))),
+		schemahcl.AliasTypeSpec("native_character", "native character", schemahcl.WithAttributes(schemahcl.SizeTypeAttr(false))),
+		schemahcl.TypeSpec("nvarchar", schemahcl.WithAttributes(schemahcl.SizeTypeAttr(false))),
+		schemahcl.TypeSpec("clob", schemahcl.WithAttributes(schemahcl.SizeTypeAttr(false))),
+		schemahcl.TypeSpec("numeric", schemahcl.WithAttributes(&schemaspec.TypeAttr{Name: "precision", Kind: reflect.Int, Required: false}, &schemaspec.TypeAttr{Name: "scale", Kind: reflect.Int, Required: false})),
+		schemahcl.TypeSpec("decimal", schemahcl.WithAttributes(&schemaspec.TypeAttr{Name: "precision", Kind: reflect.Int, Required: false}, &schemaspec.TypeAttr{Name: "scale", Kind: reflect.Int, Required: false})),
+		schemahcl.TypeSpec("boolean"),
+		schemahcl.TypeSpec("date"),
+		schemahcl.TypeSpec("datetime"),
+		schemahcl.TypeSpec("json"),
+		schemahcl.TypeSpec("uuid"),
 	),
 )
 


Test patch:
diff --git a/sql/internal/specutil/types_test.go b/schema/schemaspec/schemahcl/types_test.go
similarity index 90%
rename from sql/internal/specutil/types_test.go
rename to schema/schemaspec/schemahcl/types_test.go
index 98ee81a2..3cf4ed93 100644
--- a/sql/internal/specutil/types_test.go
+++ b/schema/schemaspec/schemahcl/types_test.go
@@ -1,10 +1,11 @@
-package specutil
+package schemahcl
 
 import (
 	"reflect"
 	"testing"
 
 	"ariga.io/atlas/schema/schemaspec"
+	"ariga.io/atlas/schema/schemaspec/internal/schemautil"
 	"ariga.io/atlas/sql/schema"
 
 	"github.com/stretchr/testify/require"
@@ -30,7 +31,7 @@ func TestTypePrint(t *testing.T) {
 		},
 		{
 			spec:     intSpec,
-			typ:      &schemaspec.Type{T: "int", Attrs: []*schemaspec.Attr{LitAttr("unsigned", "true")}},
+			typ:      &schemaspec.Type{T: "int", Attrs: []*schemaspec.Attr{schemautil.LitAttr("unsigned", "true")}},
 			expected: "int unsigned",
 		},
 		{
@@ -39,7 +40,7 @@ func TestTypePrint(t *testing.T) {
 				T:          "float",
 				Attributes: []*schemaspec.TypeAttr{unsignedTypeAttr()},
 			},
-			typ:      &schemaspec.Type{T: "float", Attrs: []*schemaspec.Attr{LitAttr("unsigned", "true")}},
+			typ:      &schemaspec.Type{T: "float", Attrs: []*schemaspec.Attr{schemautil.LitAttr("unsigned", "true")}},
 			expected: "float unsigned",
 		},
 		{
@@ -50,7 +51,7 @@ func TestTypePrint(t *testing.T) {
 					{Name: "size", Kind: reflect.Int, Required: true},
 				},
 			},
-			typ:      &schemaspec.Type{T: "varchar", Attrs: []*schemaspec.Attr{LitAttr("size", "255")}},
+			typ:      &schemaspec.Type{T: "varchar", Attrs: []*schemaspec.Attr{schemautil.LitAttr("size", "255")}},
 			expected: "varchar(255)",
 		},
 	} {
@@ -168,34 +169,34 @@ func TestRegistryConvert(t *testing.T) {
 	}{
 		{
 			typ:      &schema.StringType{T: "varchar", Size: 255},
-			expected: &schemaspec.Type{T: "varchar", Attrs: []*schemaspec.Attr{LitAttr("size", "255")}},
+			expected: &schemaspec.Type{T: "varchar", Attrs: []*schemaspec.Attr{schemautil.LitAttr("size", "255")}},
 		},
 		{
 			typ:      &schema.IntegerType{T: "int", Unsigned: true},
-			expected: &schemaspec.Type{T: "int", Attrs: []*schemaspec.Attr{LitAttr("unsigned", "true")}},
+			expected: &schemaspec.Type{T: "int", Attrs: []*schemaspec.Attr{schemautil.LitAttr("unsigned", "true")}},
 		},
 		{
 			typ:      &schema.IntegerType{T: "int", Unsigned: true},
-			expected: &schemaspec.Type{T: "int", Attrs: []*schemaspec.Attr{LitAttr("unsigned", "true")}},
+			expected: &schemaspec.Type{T: "int", Attrs: []*schemaspec.Attr{schemautil.LitAttr("unsigned", "true")}},
 		},
 		{
 			typ: &schema.DecimalType{T: "decimal", Precision: 10, Scale: 2}, // decimal(10,2)
 			expected: &schemaspec.Type{T: "decimal", Attrs: []*schemaspec.Attr{
-				LitAttr("precision", "10"),
-				LitAttr("scale", "2"),
+				schemautil.LitAttr("precision", "10"),
+				schemautil.LitAttr("scale", "2"),
 			}},
 		},
 		{
 			typ: &schema.DecimalType{T: "decimal", Precision: 10}, // decimal(10)
 			expected: &schemaspec.Type{T: "decimal", Attrs: []*schemaspec.Attr{
-				LitAttr("precision", "10"),
+				schemautil.LitAttr("precision", "10"),
 			}},
 		},
 		{
 			typ: &schema.DecimalType{T: "decimal", Scale: 2}, // decimal(0,2)
 			expected: &schemaspec.Type{T: "decimal", Attrs: []*schemaspec.Attr{
-				LitAttr("precision", "0"),
-				LitAttr("scale", "2"),
+				schemautil.LitAttr("precision", "0"),
+				schemautil.LitAttr("scale", "2"),
 			}},
 		},
 		{
@@ -205,7 +206,7 @@ func TestRegistryConvert(t *testing.T) {
 		{
 			typ: &schema.EnumType{T: "enum", Values: []string{"on", "off"}},
 			expected: &schemaspec.Type{T: "enum", Attrs: []*schemaspec.Attr{
-				ListAttr("values", `"on"`, `"off"`),
+				schemautil.ListAttr("values", `"on"`, `"off"`),
 			}},
 		},
 		{
diff --git a/sql/internal/spectest/spectest.go b/sql/internal/spectest/spectest.go
index 3bd8a7f9..22ff1a8c 100644
--- a/sql/internal/spectest/spectest.go
+++ b/sql/internal/spectest/spectest.go
@@ -9,6 +9,7 @@ import (
 	"testing"
 
 	"ariga.io/atlas/schema/schemaspec"
+	"ariga.io/atlas/schema/schemaspec/schemahcl"
 	"ariga.io/atlas/sql/internal/specutil"
 
 	"github.com/stretchr/testify/require"
@@ -16,7 +17,7 @@ import (
 
 // RegistrySanityTest runs a sanity for a TypeRegistry, generated a dummy *schemaspec.Type
 // then converting it to a schema.Type and back to a *schemaspec.Type.
-func RegistrySanityTest(t *testing.T, registry *specutil.TypeRegistry, skip []string) {
+func RegistrySanityTest(t *testing.T, registry *schemahcl.TypeRegistry, skip []string) {
 	for _, ts := range registry.Specs() {
 		if contains(ts.Name, skip) {
 			continue
diff --git a/sql/migrate/migrate_test.go b/sql/migrate/migrate_test.go
index 09b59cab..68c8c53b 100644
--- a/sql/migrate/migrate_test.go
+++ b/sql/migrate/migrate_test.go
@@ -239,6 +239,10 @@ func TestLocalDir(t *testing.T) {
 	require.ErrorContains(t, err, "sql/migrate: \"migrate.go\" is not a dir")
 	require.Nil(t, d)
 
+	d, err = migrate.NewLocalDir("foo/bar")
+	require.EqualError(t, err, "sql/migrate: stat foo/bar: no such file or directory")
+	require.Nil(t, d)
+
 	d, err = migrate.NewLocalDir(t.TempDir())
 	require.NoError(t, err)
 	require.NotNil(t, d)