Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sql/internal/sqlx: allow extending diff with fk attributes #3148

Merged
merged 1 commit into from
Sep 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions sql/internal/specutil/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ type (
Triggers func(*schema.Realm, []*sqlspec.Trigger) error
// Objects add themselves to the realm.
Objects func(*schema.Realm) error
// Optional function to extend the foreign keys.
ForeignKey func(*sqlspec.ForeignKey, *schema.ForeignKey) error
}

// SchemaFuncs represents a set of spec functions
Expand Down Expand Up @@ -159,7 +161,7 @@ func Scan(r *schema.Realm, doc *ScanDoc, funcs *ScanFuncs) error {
}
// Link the foreign keys.
for t, fks := range fks {
if err := linkForeignKeys(t, fks); err != nil {
if err := linkForeignKeys(funcs, t, fks); err != nil {
return err
}
}
Expand Down Expand Up @@ -511,7 +513,7 @@ func PrimaryKey(spec *sqlspec.PrimaryKey, parent *schema.Table) (*schema.Index,
// linkForeignKeys creates the foreign keys defined in the Table's spec by creating references
// to column in the provided Schema. It is assumed that all tables referenced FK definitions in the spec
// are reachable from the provided schema or its connected realm.
func linkForeignKeys(tbl *schema.Table, fks []*sqlspec.ForeignKey) error {
func linkForeignKeys(funcs *ScanFuncs, tbl *schema.Table, fks []*sqlspec.ForeignKey) error {
for _, spec := range fks {
fk := &schema.ForeignKey{Symbol: spec.Symbol, Table: tbl}
if spec.OnUpdate != nil {
Expand Down Expand Up @@ -546,6 +548,11 @@ func linkForeignKeys(tbl *schema.Table, fks []*sqlspec.ForeignKey) error {
fk.RefColumns = append(fk.RefColumns, c)
}
tbl.ForeignKeys = append(tbl.ForeignKeys, fk)
if funcs.ForeignKey != nil {
if err := funcs.ForeignKey(spec, fk); err != nil {
return err
}
}
}
return nil
}
Expand Down
6 changes: 6 additions & 0 deletions sql/internal/sqlx/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ type (
// ReferenceChanged reports if the foreign key referential action was
// changed. For example, action was changed from RESTRICT to CASCADE.
ReferenceChanged(from, to schema.ReferenceOption) bool

// ForeignKeyAttrChanged reports if any of the foreign-key attributes were changed.
ForeignKeyAttrChanged(from, to []schema.Attr) bool
}

// DropSchemaChanger is an optional interface allows DiffDriver to drop
Expand Down Expand Up @@ -614,6 +617,9 @@ func (d *Diff) fkChange(from, to *schema.ForeignKey) schema.ChangeKind {
if d.ReferenceChanged(from.OnDelete, to.OnDelete) {
change |= schema.ChangeDeleteAction
}
if d.ForeignKeyAttrChanged(from.Attrs, to.Attrs) {
change |= schema.ChangeAttr
}
return change
}

Expand Down
27 changes: 25 additions & 2 deletions sql/internal/sqlx/sqlx.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,15 +112,33 @@ func SchemaFKs(s *schema.Schema, rows *sql.Rows) error {
return TypedSchemaFKs[*nullString](s, rows)
}

// FKAttrScanner allows extending foreign-keys
// scanning with extra attributes.
type FKAttrScanner struct {
Columns func() []any
ScanFunc func(*schema.ForeignKey, []any) error
}

// TypedSchemaFKs is a version of SchemaFKs that allows to specify the type of
// used to scan update and delete actions from the database.
func TypedSchemaFKs[T ScanStringer](s *schema.Schema, rows *sql.Rows) error {
func TypedSchemaFKs[T ScanStringer](s *schema.Schema, rows *sql.Rows, attr ...*FKAttrScanner) error {
for rows.Next() {
var (
updateAction, deleteAction = V(new(T)), V(new(T))
name, table, column, tSchema, refTable, refColumn, refSchema string
columns = []any{
&name, &table, &column, &tSchema, &refTable, &refColumn, &refSchema, &updateAction, &deleteAction,
}
origin = len(columns)
)
if err := rows.Scan(&name, &table, &column, &tSchema, &refTable, &refColumn, &refSchema, &updateAction, &deleteAction); err != nil {
// Allows to scan extra attributes.
switch {
case len(attr) > 1:
return fmt.Errorf("unexpected number of attributes scanners: %d", len(attr))
case len(attr) == 1:
columns = append(columns, attr[0].Columns()...)
}
if err := rows.Scan(columns...); err != nil {
return err
}
t, ok := s.Table(table)
Expand Down Expand Up @@ -172,6 +190,11 @@ func TypedSchemaFKs[T ScanStringer](s *schema.Schema, rows *sql.Rows) error {
if _, ok := fk.RefColumn(rc.Name); !ok {
fk.RefColumns = append(fk.RefColumns, rc)
}
if len(attr) == 1 {
if err := attr[0].ScanFunc(fk, columns[origin:]); err != nil {
return err
}
}
}
return nil
}
Expand Down
5 changes: 5 additions & 0 deletions sql/mysql/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,11 @@ func (*diff) ReferenceChanged(from, to schema.ReferenceOption) bool {
return from != to
}

// ForeignKeyAttrChanged reports if any of the foreign-key attributes were changed.
func (*diff) ForeignKeyAttrChanged(_, _ []schema.Attr) bool {
return false
}

// Normalize implements the sqlx.Normalizer interface.
func (d *diff) Normalize(from, to *schema.Table, opts *schema.DiffOptions) error {
if opts.Mode.Is(schema.DiffModeNormalized) {
Expand Down
6 changes: 6 additions & 0 deletions sql/schema/dsl.go
Original file line number Diff line number Diff line change
Expand Up @@ -926,6 +926,12 @@ func (f *ForeignKey) SetOnDelete(o ReferenceOption) *ForeignKey {
return f
}

// AddAttrs adds additional attributes to the schema.
func (f *ForeignKey) AddAttrs(attrs ...Attr) *ForeignKey {
f.Attrs = append(f.Attrs, attrs...)
return f
}

// AddDeps adds the given objects as dependencies to the function.
func (f *Func) AddDeps(objs ...Object) *Func {
f.Deps = append(f.Deps, objs...)
Expand Down
1 change: 1 addition & 0 deletions sql/schema/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ type (
RefColumns []*Column
OnUpdate ReferenceOption
OnDelete ReferenceOption
Attrs []Attr
}

// A Trigger represents a trigger definition.
Expand Down
5 changes: 5 additions & 0 deletions sql/sqlite/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,11 @@ func (*diff) ReferenceChanged(from, to schema.ReferenceOption) bool {
return from != to
}

// ForeignKeyAttrChanged reports if any of the foreign-key attributes were changed.
func (*diff) ForeignKeyAttrChanged(_, _ []schema.Attr) bool {
return false
}

// Normalize implements the sqlx.Normalizer interface.
func (d *diff) Normalize(from, to *schema.Table, _ *schema.DiffOptions) error {
used := make([]bool, len(to.ForeignKeys))
Expand Down