Skip to content

Commit

Permalink
sql/schema: allow schema loaders to attach positions (#3197)
Browse files Browse the repository at this point in the history
  • Loading branch information
a8m authored Oct 20, 2024
1 parent f2dfb2f commit 3170dd8
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 4 deletions.
21 changes: 17 additions & 4 deletions schemahcl/extension.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"strings"
"sync"

"github.com/hashicorp/hcl/v2"
"github.com/zclconf/go-cty/cty"
"github.com/zclconf/go-cty/cty/gocty"
)
Expand Down Expand Up @@ -96,10 +97,12 @@ func (r *Resource) As(target any) error {
return r.as(target)
}

var typeRange = reflect.TypeOf(&hcl.Range{})

// As reads the attributes and children resources of the resource into the target struct.
func (r *Resource) as(target any) error {
existingAttrs, existingChildren := existingElements(r)
var seenName, seenQualifier bool
var seenName, seenQualifier, setRange bool
v := reflect.ValueOf(target).Elem()
for _, ft := range specFields(target) {
field := v.FieldByName(ft.Name)
Expand All @@ -119,6 +122,14 @@ func (r *Resource) as(target any) error {
}
seenQualifier = true
field.SetString(r.Qualifier)
case ft.isRange() && !hasAttr(r, ft.tag):
if field.Type() != typeRange {
return fmt.Errorf("schemahcl: expected field %q to be of type *hcl.Range: %v", ft.Name, field.Type())
}
if r.rang != nil {
setRange = true
field.Set(reflect.ValueOf(r.rang))
}
case hasAttr(r, ft.tag):
attr, _ := r.Attr(ft.tag)
if err := setField(field, attr); err != nil {
Expand Down Expand Up @@ -221,9 +232,9 @@ func (r *Resource) as(target any) error {
children := childrenOfType(r, childType)
extras.Children = append(extras.Children, children...)
}
// In case the resource contains a remain (DefaultExtension)
// field, attach to it the position.
if r.rang != nil && rem.Remain() != nil {
// In case the resource contains a remain (DefaultExtension) and
// the range was not explicitly set, attach to it the position.
if r.rang != nil && rem.Remain() != nil && !setRange {
rem.Remain().SetRange(r.rang)
}
return nil
Expand Down Expand Up @@ -613,6 +624,8 @@ func (f fieldDesc) isName() bool { return f.is("name") }

func (f fieldDesc) isQualifier() bool { return f.is("qualifier") }

func (f fieldDesc) isRange() bool { return f.is("range") }

func (f fieldDesc) omitempty() bool { return f.is("omitempty") }

func (f fieldDesc) is(t string) bool {
Expand Down
10 changes: 10 additions & 0 deletions schemahcl/schemahcl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1138,6 +1138,11 @@ func Test_WithPos(t *testing.T) {
A int `spec:"a"`
DefaultExtension
} `spec:"bar"`
Qux struct {
Range *hcl.Range `spec:",range"`
A int `spec:"a"`
DefaultExtension
} `spec:"qux"`
DefaultExtension
}
b = []byte(`
Expand All @@ -1151,6 +1156,9 @@ baz = 1
bar {
a = 1
}
qux {
a = 1
}
`)
)
require.NoError(t, New(WithPos()).EvalBytes(b, &doc, nil))
Expand All @@ -1164,6 +1172,8 @@ bar {
require.Equal(t, 4, rs[1].Children[0].Range().Start.Line)
require.Equal(t, 5, rs[1].Children[0].Attrs[0].Range().Start.Line)
require.NotNil(t, doc.Bar.Extra.Range(), "position should be attached to the resource")
require.Equal(t, doc.Qux.Range.Start.Line, 12)
require.Nil(t, doc.Qux.Extra.Range(), "position should not be attached if it was explicitly set")
}

func TestExtendedBlockDef(t *testing.T) {
Expand Down
9 changes: 9 additions & 0 deletions schemahcl/spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -609,3 +609,12 @@ func StringEnumsAttr(k string, elems ...*EnumString) *Attr {
V: cty.ListVal(vv),
}
}

// RangeAsPos builds a schema position from the give HCL range.
func RangeAsPos(r hcl.Range) *schema.Pos {
return &schema.Pos{
Filename: r.Filename,
Start: r.Start,
End: r.End,
}
}
17 changes: 17 additions & 0 deletions schemahcl/spec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import (
"strconv"
"testing"

"ariga.io/atlas/sql/schema"

"github.com/hashicorp/hcl/v2"
"github.com/stretchr/testify/require"
)

Expand Down Expand Up @@ -148,3 +151,17 @@ func TestBuildRef(t *testing.T) {
})
}
}

func TestRangeAsPos(t *testing.T) {
p := RangeAsPos(hcl.Range{
Filename: "schema.pg.hcl",
})
require.Equal(t, &schema.Pos{Filename: "schema.pg.hcl"}, p)

p = RangeAsPos(hcl.Range{
Filename: "schema.pg.hcl",
Start: hcl.Pos{Byte: 10},
End: hcl.Pos{Byte: 20},
})
require.Equal(t, &schema.Pos{Filename: "schema.pg.hcl", Start: hcl.Pos{Byte: 10}, End: hcl.Pos{Byte: 20}}, p)
}
12 changes: 12 additions & 0 deletions sql/schema/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,17 @@ type (
Materialized struct {
Attr
}

// Pos is an attribute that holds the position of a schema element.
Pos struct {
// Filename is the name (or full path) of the file which loaded the schema element.
Filename string

// Start and End represent the bounds of this range.
Start, End struct {
Line, Column, Byte int // hcl.Pos fields.
}
}
)

// A list of known view check options.
Expand Down Expand Up @@ -650,6 +661,7 @@ func (*DecimalType) typ() {}
func (*UnsupportedType) typ() {}

// attributes.
func (*Pos) attr() {}
func (*Check) attr() {}
func (*Comment) attr() {}
func (*Charset) attr() {}
Expand Down
13 changes: 13 additions & 0 deletions sql/sqlspec/sqlspec.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

"ariga.io/atlas/schemahcl"

"github.com/hashicorp/hcl/v2"
"github.com/zclconf/go-cty/cty"
)

Expand All @@ -31,6 +32,7 @@ type (
Indexes []*Index `spec:"index"`
Checks []*Check `spec:"check"`
schemahcl.DefaultExtension
Range *hcl.Range `spec:",range"`
}

// View holds a specification for an SQL view.
Expand All @@ -45,6 +47,7 @@ type (
// The definition is appended as additional attribute
// by the spec creator to marshal it after the columns.
schemahcl.DefaultExtension
Range *hcl.Range `spec:",range"`
}

// Column holds a specification for a column in an SQL table.
Expand All @@ -54,13 +57,15 @@ type (
Type *schemahcl.Type `spec:"type"`
Default cty.Value `spec:"default"`
schemahcl.DefaultExtension
Range *hcl.Range `spec:",range"`
}

// PrimaryKey holds a specification for the primary key of a table.
PrimaryKey struct {
Parts []*IndexPart `spec:"on"`
Columns []*schemahcl.Ref `spec:"columns"`
schemahcl.DefaultExtension
Range *hcl.Range `spec:",range"`
}

// Index holds a specification for the index key of a table.
Expand All @@ -70,6 +75,7 @@ type (
Parts []*IndexPart `spec:"on"`
Columns []*schemahcl.Ref `spec:"columns"`
schemahcl.DefaultExtension
Range *hcl.Range `spec:",range"`
}

// IndexPart holds a specification for the index key part.
Expand All @@ -78,13 +84,15 @@ type (
Column *schemahcl.Ref `spec:"column"`
Expr string `spec:"expr,omitempty"`
schemahcl.DefaultExtension
Range *hcl.Range `spec:",range"`
}

// Check holds a specification for a check constraint on a table.
Check struct {
Name string `spec:",name"`
Expr string `spec:"expr"`
schemahcl.DefaultExtension
Range *hcl.Range `spec:",range"`
}

// ForeignKey holds a specification for the Foreign key of a table.
Expand All @@ -95,6 +103,7 @@ type (
OnUpdate *schemahcl.Ref `spec:"on_update"`
OnDelete *schemahcl.Ref `spec:"on_delete"`
schemahcl.DefaultExtension
Range *hcl.Range `spec:",range"`
}

// Func holds the specification for a function.
Expand All @@ -107,6 +116,7 @@ type (
// The definition and the return type are appended as additional
// attribute by the spec creator to marshal it after the arguments.
schemahcl.DefaultExtension
Range *hcl.Range `spec:",range"`
}

// FuncArg holds the specification for a function argument.
Expand All @@ -117,6 +127,7 @@ type (
// Optional attributes such as mode are added by the driver,
// as their definition can be either a string or an enum (ref).
schemahcl.DefaultExtension
Range *hcl.Range `spec:",range"`
}

// Trigger holds the specification for a trigger.
Expand All @@ -125,6 +136,7 @@ type (
On *schemahcl.Ref `spec:"on"` // A table or a view.
// Attributes and blocks are different for each driver.
schemahcl.DefaultExtension
Range *hcl.Range `spec:",range"`
}

// Sequence holds a specification for a Sequence.
Expand All @@ -135,6 +147,7 @@ type (
// Type, Start, Increment, Min, Max, Cache, Cycle
// are optionally added to the sequence definition.
schemahcl.DefaultExtension
Range *hcl.Range `spec:",range"`
}
)

Expand Down

0 comments on commit 3170dd8

Please sign in to comment.