Skip to content

Commit

Permalink
Fix: nested recursive relationships consumed by first reference
Browse files Browse the repository at this point in the history
  • Loading branch information
lazer-nikolic committed Sep 16, 2024
1 parent f80d099 commit 0b9ea66
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 23 deletions.
22 changes: 14 additions & 8 deletions internal/generate/export.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,15 +217,21 @@ func BuildDIYMethod(f *parser.InterfaceSet, s *QueryStructMeta, data []*Interfac
// ParseStructRelationShip parse struct's relationship
// No one should use it directly in project
func ParseStructRelationShip(relationship *schema.Relationships) []field.Relation {
cache := make(map[string]bool)
relationCache := make(map[string][]field.Relation)
return append(append(append(append(
make([]field.Relation, 0, 4),
pullRelationShip(cache, relationCache, relationship.HasOne)...),
pullRelationShip(cache, relationCache, relationship.HasMany)...),
pullRelationShip(cache, relationCache, relationship.BelongsTo)...),
pullRelationShip(cache, relationCache, relationship.Many2Many)...,
cache := make(map[string]map[schema.RelationshipType]field.Relation)
incomplete := make([]incompleteCb, 0)
relations := append(append(append(append(
make([]field.Relation, 0),
pullRelationShip(cache, &incomplete, relationship.HasOne)...),
pullRelationShip(cache, &incomplete, relationship.HasMany)...),
pullRelationShip(cache, &incomplete, relationship.BelongsTo)...),
pullRelationShip(cache, &incomplete, relationship.Many2Many)...,
)

for i := range incomplete {
incomplete[i].Call(cache)
}

return relations
}

// GetStructNames get struct names from base structs
Expand Down
65 changes: 50 additions & 15 deletions internal/generate/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -246,29 +246,64 @@ func isStructType(data reflect.Value) bool {
(data.Kind() == reflect.Ptr && data.Elem().Kind() == reflect.Struct)
}

func pullRelationShip(cache map[string]bool, relationCache map[string][]field.Relation, relationships []*schema.Relationship) []field.Relation {
type incompleteCb struct {
result *field.Relation
relationship *schema.Relationship
incomplete *[]incompleteCb
}

func (cb *incompleteCb) Call(cache map[string]map[schema.RelationshipType]field.Relation) field.Relation {
varType := strings.TrimLeft(cb.relationship.Field.FieldType.String(), "[]*")
childRelations := pullRelationShip(cache, cb.incomplete,
append(append(append(append(
make([]*schema.Relationship, 0, 4),
cb.relationship.FieldSchema.Relationships.BelongsTo...),
cb.relationship.FieldSchema.Relationships.HasOne...),
cb.relationship.FieldSchema.Relationships.HasMany...),
cb.relationship.FieldSchema.Relationships.Many2Many...),
)
return *field.NewRelationWithType(field.RelationshipType(cb.relationship.Type), cb.relationship.Name, varType, childRelations...)
}

func pullRelationShip(cache map[string]map[schema.RelationshipType]field.Relation, incomplete *[]incompleteCb, relationships []*schema.Relationship) []field.Relation {
if len(relationships) == 0 {
return nil
}
result := make([]field.Relation, len(relationships))
for i, relationship := range relationships {
var childRelations []field.Relation
var (
childRelations []field.Relation
cacheHit bool
varTypeHit bool
)
varType := strings.TrimLeft(relationship.Field.FieldType.String(), "[]*")
if !cache[varType] {
cache[varType] = true
childRelations = pullRelationShip(cache, relationCache, append(append(append(append(
make([]*schema.Relationship, 0, 4),
relationship.FieldSchema.Relationships.BelongsTo...),
relationship.FieldSchema.Relationships.HasOne...),
relationship.FieldSchema.Relationships.HasMany...),
relationship.FieldSchema.Relationships.Many2Many...),
_, varTypeHit = cache[varType]
if !varTypeHit {
cache[varType] = make(map[schema.RelationshipType]field.Relation)
}
result[i], cacheHit = cache[varType][relationship.Type]
if !cacheHit {
// "Lock" the cache so it does not go into infinite recursion
cache[varType][relationship.Type] = field.Relation{}
childRelations = pullRelationShip(cache, incomplete,
append(append(append(append(
make([]*schema.Relationship, 0, 4),
relationship.FieldSchema.Relationships.BelongsTo...),
relationship.FieldSchema.Relationships.HasOne...),
relationship.FieldSchema.Relationships.HasMany...),
relationship.FieldSchema.Relationships.Many2Many...),
)
relationCache[varType] = childRelations

} else {
childRelations = relationCache[varType]
result[i] = *field.NewRelationWithType(field.RelationshipType(relationship.Type), relationship.Name, varType, childRelations...)
cache[varType][relationship.Type] = result[i]
}
if result[i].Name() == "" {
result[i] = *field.NewRelationWithType(field.RelationshipType(relationship.Type), relationship.Name, varType)
*incomplete = append(*incomplete, incompleteCb{
result: &result[i],
relationship: relationship,
incomplete: incomplete,
})
}
result[i] = *field.NewRelationWithType(field.RelationshipType(relationship.Type), relationship.Name, varType, childRelations...)
}
return result
}

0 comments on commit 0b9ea66

Please sign in to comment.