Repository: ariga/atlas
Pull request #291 (2021-12-07 19:33:02)

schema/schemaspec/schemahcl: don't break on type attributes that aren't defined as type arguments in sql
this is infra to support `TypeSpec`s that have attributes that aren't represented as "type arguments" (for example `unsigned` in integer types


Patch:
diff --git a/schema/schemaspec/schemahcl/hcl.go b/schema/schemaspec/schemahcl/hcl.go
index 4392898a..885ad871 100644
--- a/schema/schemaspec/schemahcl/hcl.go
+++ b/schema/schemaspec/schemahcl/hcl.go
@@ -292,11 +292,15 @@ func (s *state) findTypeSpec(t string) (*schemaspec.TypeSpec, bool) {
 }
 
 func hclType(spec *schemaspec.TypeSpec, typ *schemaspec.Type) (string, error) {
-	if len(spec.Attributes) == 0 {
+	if len(typeFuncArgs(spec)) == 0 {
 		return typ.T, nil
 	}
 	args := make([]string, 0, len(spec.Attributes))
-	for _, arg := range typ.Attributes {
+	for _, param := range typeFuncArgs(spec) {
+		arg, ok := findAttr(typ.Attributes, param.Name)
+		if !ok {
+			continue
+		}
 		lit, ok := arg.V.(*schemaspec.LiteralValue)
 		if !ok {
 			return "", errors.New("expecting literal value")
@@ -306,6 +310,15 @@ func hclType(spec *schemaspec.TypeSpec, typ *schemaspec.Type) (string, error) {
 	return fmt.Sprintf("%s(%s)", typ.T, strings.Join(args, ",")), nil
 }
 
+func findAttr(attrs []*schemaspec.Attr, k string) (*schemaspec.Attr, bool) {
+	for _, attr := range attrs {
+		if attr.K == k {
+			return attr, true
+		}
+	}
+	return nil, false
+}
+
 func hclRawTokens(s string) hclwrite.Tokens {
 	return hclwrite.Tokens{
 		&hclwrite.Token{
diff --git a/schema/schemaspec/schemahcl/opts.go b/schema/schemaspec/schemahcl/opts.go
index 7dc6d3f8..0e3e0d0c 100644
--- a/schema/schemaspec/schemahcl/opts.go
+++ b/schema/schemaspec/schemahcl/opts.go
@@ -46,7 +46,7 @@ func WithTypes(typeSpecs []*schemaspec.TypeSpec) Option {
 	return func(config *Config) {
 		for _, typeSpec := range typeSpecs {
 			config.types = append(config.types, typeSpec)
-			if len(typeSpec.Attributes) == 0 {
+			if len(typeFuncArgs(typeSpec)) == 0 {
 				typ := &schemaspec.Type{T: typeSpec.T}
 				config.ctx.Variables[typeSpec.Name] = cty.CapsuleVal(ctyTypeSpec, typ)
 				continue
@@ -54,7 +54,7 @@ func WithTypes(typeSpecs []*schemaspec.TypeSpec) Option {
 			spec := &function.Spec{
 				Type: function.StaticReturnType(ctyTypeSpec),
 			}
-			for _, arg := range typeSpec.Attributes {
+			for _, arg := range typeFuncArgs(typeSpec) {
 				p := function.Parameter{
 					Name:      arg.Name,
 					AllowNull: !arg.Required,
@@ -86,3 +86,17 @@ func WithTypes(typeSpecs []*schemaspec.TypeSpec) Option {
 		}
 	}
 }
+
+// typeFuncArgs returns the type attributes that are configured via arguments to the
+// type definition, for example precision and scale in a decimal definition, i.e `decimal(10,2)`.
+func typeFuncArgs(spec *schemaspec.TypeSpec) []*schemaspec.TypeAttr {
+	var args []*schemaspec.TypeAttr
+	for _, attr := range spec.Attributes {
+		// TODO(rotemtam): this should be defined on the TypeSpec.
+		if attr.Name == "unsigned" {
+			continue
+		}
+		args = append(args, attr)
+	}
+	return args
+}


Test patch:
diff --git a/schema/schemaspec/schemahcl/context_test.go b/schema/schemaspec/schemahcl/context_test.go
index b7dfc4b6..030e9583 100644
--- a/schema/schemaspec/schemahcl/context_test.go
+++ b/schema/schemaspec/schemahcl/context_test.go
@@ -359,8 +359,14 @@ sized  = varchar(255)
 	s := New(
 		WithTypes(
 			[]*schemaspec.TypeSpec{
-				{Name: "int", T: "int"},
 				{Name: "bool", T: "bool"},
+				{
+					Name: "int",
+					T:    "int",
+					Attributes: []*schemaspec.TypeAttr{
+						{Name: "unsigned", Kind: reflect.Bool, Required: false},
+					},
+				},
 				{
 					Name: "varchar",
 					T:    "varchar",