Repository navigation
executor: adds support for anonymous contained structs - #371
chris-ramon wants to merge 2 commits into
Conversation
|
HI, wondering if there is any chance to merge this? |
|
@chris-ramon @pavelnikolov Same question, would you please merge it? |
|
Same here |
|
Thanks for letting us know how important this PR are for you guys. I'll jump back to get this merge as soon as I get the chance today/tomorrow. |
d376817 to
d35d558
Compare
These should behave similarly to the equivalent case in https://golang.org/pkg/encoding/json/
defaultResolveStruct from DefaultResolveFn
|
I think this will resolve the issue without using user-provided resolve function. if typeField.Anonymous && typeField.Type.Kind() == reflect.Struct {
anonymousResolvedValue, err := defaultResolveStruct(valueField, fieldName)
if err != nil {
return nil, err
} else if anonymousResolvedValue != nil {
return anonymousResolvedValue, nil
}
} |
|
Just commented on the issue, but just saw this PR. I'm still interested in this! |
| return valueField.Interface(), nil | ||
| } | ||
| if typeField.Anonymous && typeField.Type.Kind() == reflect.Struct { | ||
| return defaultResolveStruct(valueField, fieldName) |
There was a problem hiding this comment.
There's a bug here. We return too early if an anonymous struct comes before a field we're looking for.
I think what we want to do is run through all the top-level fields first, then dive if the field wasn't found.
Repeat for all anonymous structs until it's found. If not found, then return nil, nil.
There was a problem hiding this comment.
Here's a fix, sans a proper test:
|
Any news on this ? |
|
Any updates on this PR? |
|
Still waiting for the merge as qell... |
|
This PR has conflicts. |
|
Is this still being worked on? |
|
Not that I am aware of |
|
Hi |
|
Bump |
DefaultResolveFn only scanned a struct's directly declared fields, so a
field promoted from an embedded struct resolved to null:
type Inner struct{ Name string `json:"name"` }
type Outer struct{ Inner; Other string }
{ o { name other } } -> {"name": null, "other": "outer"}
Match Go's own promotion rules rather than a first-match recursion: the
search walks the embedding graph breadth-first, so a shallower field
wins outright and a same-depth tie between two branches is ambiguous
and matches nothing — the answer a plain selector expression would give.
A depth-first walk would instead let a deeply embedded field shadow a
shallower one purely by declaration order.
resolveDefaultStructField now returns an index path ([]int) instead of a
single index, and defaultResolveCache memoizes that path. Walking it goes
through fieldByIndexPath rather than reflect.Value.FieldByIndex, which
panics when the path crosses a nil embedded pointer; a nil embed yields a
null field. Self-referential embeddings (type T struct{ *T }) terminate
via a visited-type set.
Only anonymous fields promote — a named struct field is still not
searched.
No hot-path cost: PlannedAppend_DefaultResolve_Struct50 is 8.98µs before
and after (~, p=0.589, n=6), with B/op and allocs/op unchanged. The cache
turns the wider search into a one-off per (type, field name).
Addresses the problem in graphql-go#371, which is still open
upstream. Not cherry-picked: that PR's defaultResolveStruct returns the
result of the first anonymous field it finds even when that returns no
match, so a later sibling holding the field is never reached, and it
drops this fork's cached index in favour of a per-call scan.
Triaged all 38 open PRs on graphql-go/graphql. Records which are already present here (15, several via our own independent fixes), the one acted on (graphql-go#371), and why the rest are not worth taking — so the next sync re-checks only PRs opened since.
Overview
Test plan
go test ./...