-
-
Notifications
You must be signed in to change notification settings - Fork 104
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
Implement field merging #472
Conversation
For additional context, I've added Additionally, I've converted However, this does increase the heap allocation size and I'm probably not doing the best thing. Any suggestions to improve this would be appreciated.
|
c4ab793
to
f6b333d
Compare
incredible stuff @bryanmylee! I'll work through this tomorrow and follow up with any feedback I'd like to get @imor 's take on this too since he's a much stronger rust developer. he's currently out sick so it might be a few days before he's able to get to it. just keeping you in the loop |
src/parser_util.rs
Outdated
pub fn has_same_type_shape( | ||
field_name: &str, | ||
type_name: &str, | ||
type_a: &__Type, | ||
type_b: &__Type, | ||
) -> Result<(), String> { | ||
let mut type_a = type_a; | ||
let mut type_b = type_b; | ||
|
||
if matches!(type_a, __Type::NonNull(_)) || matches!(type_b, __Type::NonNull(_)) { | ||
if let (__Type::NonNull(nullable_type_a), __Type::NonNull(nullable_type_b)) = | ||
(type_a, type_b) | ||
{ | ||
type_a = nullable_type_a.type_.as_ref(); | ||
type_b = nullable_type_b.type_.as_ref(); | ||
} else { | ||
return Err(format!( | ||
"Fields '{}' conflict because only one is non nullable", | ||
field_name, | ||
)); | ||
} | ||
} | ||
|
||
if matches!(type_a, __Type::List(_)) || matches!(type_b, __Type::List(_)) { | ||
if let (__Type::List(list_type_a), __Type::List(list_type_b)) = (type_a, type_b) { | ||
type_a = list_type_a.type_.as_ref(); | ||
type_b = list_type_b.type_.as_ref(); | ||
} else { | ||
return Err(format!( | ||
"Fields '{}' conflict because only one is a list type", | ||
field_name, | ||
)); | ||
} | ||
|
||
return has_same_type_shape(field_name, type_name, type_a, type_b); | ||
} | ||
|
||
if matches!(type_a, __Type::Enum(_)) | ||
|| matches!(type_b, __Type::Enum(_)) | ||
|| matches!(type_a, __Type::Scalar(_)) | ||
|| matches!(type_b, __Type::Scalar(_)) | ||
{ | ||
return if type_a == type_b { | ||
Ok(()) | ||
} else { | ||
Err(format!( | ||
"Fields '{}' conflict due to mismatched types", | ||
field_name, | ||
)) | ||
}; | ||
} | ||
|
||
// TODO handle composite types? | ||
|
||
// Subfield type shapes will be checked on a later pass. | ||
Ok(()) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is confirming that the __Type
of an and b are the same. As I understand, they need to be the exact same __Type
in order to merge, correct?
If so, could we do something like make __Type
hashable or equatable so they can be directly compared without this logic? The reason I prefer that kind of solution is because it would fail loudly if we introduce new __Types
vs quietly not working with merging
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, this is a check to make sure both types have the same shape following the GQL specification linked in the discussion thread.
I did this explicitly because it gives me the ability to return specific errors for the different type conflicts, and keep the system close to spec.
I'm not sure if we should consider all __Type
variants in the sameness check since it's not in spec, but an Eq
trait would imply that the values are equal across across all variants.
However, I do agree it should be co-located closer to the __Type
definition.
Another thing I just noticed was that I forgot to include type_name
in the error messages so it's an unused variable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if we should consider all __Type variants in the sameness check since it's not in spec, but an Eq trait would imply that the values are equal across across all variants.
That's interesting, I didn't quite follow. Could I hassle you to give an example that highlights the issue?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My guess was if a __Type::Edge
and __Type::Node
are compared, they should not fail the shape sameness check yet, as the __Type::Node
might have a compatible set of fields for a selection to be merged with the __Type::Edge
.
Of course, I just realized this case would rarely be encountered since field selections have to have the same field name anyways, so I don't see where this issue might occur.
With that in mind, we can definitely just use a Eq
check, unless we want to provide better error messages?
What kind of change does this PR introduce?
Introduces field merging to properly combine multiple selections of the same object fields. This allows queries like:
... to resolve correctly to:
This PR also handles the case where two overlapping fields have different arguments, returning an error in the case.
What is the current behavior?
Object fields that appear more than once will override each other, resulting in only the last-seen field being returned.
This also affects #237 since overlapping fragment subqueries behave like overlapping fields.
What is missing?
Overlapping fields are merged correctly.
However, I still need to figure out how to determine the return type of a given field at the parser level so that I can return an error if two conflicting fields with different data types / shapes are merged with aliases.There is also the issue of updating the positions and spans of a merged field. It doesn't affect the final output, but it may be useful in the future to define either a minimally containing span for all fields that make up a merged field, or store a vector of positions and spans.