Skip to content

Commit f7faf0e

Browse files
committed
graph, graphql: Use our own structs to represent the query AST
This change should not change any behavior, and is purely mechanical in replacing one set of structs with another set of identical structs that we control
1 parent 34dc38e commit f7faf0e

File tree

16 files changed

+493
-250
lines changed

16 files changed

+493
-250
lines changed

graph/src/components/store.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1798,12 +1798,11 @@ impl AttributeNames {
17981798
}
17991799
}
18001800

1801-
/// Adds a attribute name. Ignores meta fields.
1802-
pub fn add(&mut self, field: &q::Field) {
1803-
if Self::is_meta_field(&field.name) {
1801+
pub fn update(&mut self, field_name: &str) {
1802+
if Self::is_meta_field(field_name) {
18041803
return;
18051804
}
1806-
self.insert(&field.name)
1805+
self.insert(&field_name)
18071806
}
18081807

18091808
/// Adds a attribute name. Ignores meta fields.

graphql/src/execution/ast.rs

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
use std::ops::Deref;
2+
3+
use graph::prelude::{q, r};
4+
use graphql_parser::Pos;
5+
6+
#[derive(Debug, Clone, PartialEq)]
7+
pub struct FragmentDefinition {
8+
pub position: Pos,
9+
pub name: String,
10+
pub type_condition: TypeCondition,
11+
pub directives: Vec<Directive>,
12+
pub selection_set: SelectionSet,
13+
}
14+
15+
#[derive(Debug, Clone, PartialEq)]
16+
pub struct SelectionSet {
17+
pub span: (Pos, Pos),
18+
pub items: Vec<Selection>,
19+
}
20+
21+
#[derive(Debug, Clone, PartialEq)]
22+
pub enum Selection {
23+
Field(Field),
24+
FragmentSpread(FragmentSpread),
25+
InlineFragment(InlineFragment),
26+
}
27+
28+
impl Selection {
29+
/// Looks up a directive in a selection, if it is provided.
30+
pub fn get_directive(&self, name: &str) -> Option<&Directive> {
31+
match self {
32+
Selection::Field(field) => field
33+
.directives
34+
.iter()
35+
.find(|directive| directive.name == name),
36+
_ => None,
37+
}
38+
}
39+
40+
/// Returns true if a selection should be skipped (as per the `@skip` directive).
41+
pub fn skip(&self) -> bool {
42+
match self.get_directive("skip") {
43+
Some(directive) => match directive.argument_value("if") {
44+
Some(val) => match val {
45+
// Skip if @skip(if: true)
46+
r::Value::Boolean(skip_if) => *skip_if,
47+
_ => false,
48+
},
49+
None => true,
50+
},
51+
None => false,
52+
}
53+
}
54+
55+
/// Returns true if a selection should be included (as per the `@include` directive).
56+
pub fn include(&self) -> bool {
57+
match self.get_directive("include") {
58+
Some(directive) => match directive.argument_value("if") {
59+
Some(val) => match val {
60+
// Include if @include(if: true)
61+
r::Value::Boolean(include) => *include,
62+
_ => false,
63+
},
64+
None => true,
65+
},
66+
None => true,
67+
}
68+
}
69+
}
70+
71+
#[derive(Debug, Clone, PartialEq)]
72+
pub struct Directive {
73+
pub position: Pos,
74+
pub name: String,
75+
pub arguments: Vec<(String, r::Value)>,
76+
}
77+
78+
impl Directive {
79+
/// Looks up the value of an argument in a vector of (name, value) tuples.
80+
pub fn argument_value(&self, name: &str) -> Option<&r::Value> {
81+
self.arguments
82+
.iter()
83+
.find(|(n, _)| n == name)
84+
.map(|(_, v)| v)
85+
}
86+
}
87+
88+
#[derive(Debug, Clone, PartialEq)]
89+
pub struct Field {
90+
pub position: Pos,
91+
pub alias: Option<String>,
92+
pub name: String,
93+
pub arguments: Vec<(String, r::Value)>,
94+
pub directives: Vec<Directive>,
95+
pub selection_set: SelectionSet,
96+
}
97+
98+
impl Field {
99+
/// Returns the response key of a field, which is either its name or its alias (if there is one).
100+
pub fn response_key(&self) -> &str {
101+
self.alias
102+
.as_ref()
103+
.map(Deref::deref)
104+
.unwrap_or(self.name.as_str())
105+
}
106+
107+
/// Looks up the value of an argument in a vector of (name, value) tuples.
108+
pub fn argument_value(&self, name: &str) -> Option<&r::Value> {
109+
self.arguments
110+
.iter()
111+
.find(|(n, _)| n == name)
112+
.map(|(_, v)| v)
113+
}
114+
}
115+
116+
#[derive(Debug, Clone, PartialEq)]
117+
pub struct FragmentSpread {
118+
pub position: Pos,
119+
pub fragment_name: String,
120+
pub directives: Vec<Directive>,
121+
}
122+
123+
#[derive(Debug, Clone, PartialEq)]
124+
pub enum TypeCondition {
125+
On(String),
126+
}
127+
128+
impl From<q::TypeCondition> for TypeCondition {
129+
fn from(type_cond: q::TypeCondition) -> Self {
130+
let q::TypeCondition::On(name) = type_cond;
131+
TypeCondition::On(name)
132+
}
133+
}
134+
135+
#[derive(Debug, Clone, PartialEq)]
136+
pub struct InlineFragment {
137+
pub position: Pos,
138+
pub type_condition: Option<TypeCondition>,
139+
pub directives: Vec<Directive>,
140+
pub selection_set: SelectionSet,
141+
}

graphql/src/execution/execution.rs

Lines changed: 34 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@ use graph::prelude::*;
2121
use graph::util::lfu_cache::LfuCache;
2222

2323
use super::QueryHash;
24+
use crate::execution::ast as a;
2425
use crate::introspection::{
2526
is_introspection_field, INTROSPECTION_DOCUMENT, INTROSPECTION_QUERY_TYPE,
2627
};
2728
use crate::prelude::*;
28-
use crate::query::ast as qast;
2929
use crate::schema::ast as sast;
3030
use crate::values::coercion;
3131

@@ -136,8 +136,8 @@ impl Default for WeightedResult {
136136

137137
struct HashableQuery<'a> {
138138
query_schema_id: &'a DeploymentHash,
139-
query_fragments: &'a HashMap<String, q::FragmentDefinition>,
140-
selection_set: &'a q::SelectionSet,
139+
query_fragments: &'a HashMap<String, a::FragmentDefinition>,
140+
selection_set: &'a a::SelectionSet,
141141
block_ptr: &'a BlockPtr,
142142
}
143143

@@ -165,14 +165,12 @@ impl StableHash for HashableQuery<'_> {
165165
// Not stable! Uses to_string()
166166
self.query_fragments
167167
.iter()
168-
.map(|(k, v)| (k, v.to_string()))
168+
.map(|(k, v)| (k, format!("{:?}", v)))
169169
.collect::<HashMap<_, _>>()
170170
.stable_hash(sequence_number.next_child(), state);
171171

172172
// Not stable! Uses to_string
173-
self.selection_set
174-
.to_string()
175-
.stable_hash(sequence_number.next_child(), state);
173+
format!("{:?}", self.selection_set).stable_hash(sequence_number.next_child(), state);
176174

177175
self.block_ptr
178176
.stable_hash(sequence_number.next_child(), state);
@@ -182,7 +180,7 @@ impl StableHash for HashableQuery<'_> {
182180
// The key is: subgraph id + selection set + variables + fragment definitions
183181
fn cache_key(
184182
ctx: &ExecutionContext<impl Resolver>,
185-
selection_set: &q::SelectionSet,
183+
selection_set: &a::SelectionSet,
186184
block_ptr: &BlockPtr,
187185
) -> QueryHash {
188186
// It is very important that all data used for the query is included.
@@ -279,24 +277,24 @@ where
279277

280278
pub fn execute_root_selection_set_uncached(
281279
ctx: &ExecutionContext<impl Resolver>,
282-
selection_set: &q::SelectionSet,
280+
selection_set: &a::SelectionSet,
283281
root_type: &s::ObjectType,
284282
) -> Result<Object, Vec<QueryExecutionError>> {
285283
// Split the top-level fields into introspection fields and
286284
// regular data fields
287-
let mut data_set = q::SelectionSet {
285+
let mut data_set = a::SelectionSet {
288286
span: selection_set.span,
289287
items: Vec::new(),
290288
};
291-
let mut intro_set = q::SelectionSet {
289+
let mut intro_set = a::SelectionSet {
292290
span: selection_set.span,
293291
items: Vec::new(),
294292
};
295293
let mut meta_items = Vec::new();
296294

297295
for (_, fields) in collect_fields(ctx, root_type, iter::once(selection_set)) {
298296
let name = fields[0].name.clone();
299-
let selections = fields.into_iter().map(|f| q::Selection::Field(f.clone()));
297+
let selections = fields.into_iter().map(|f| a::Selection::Field(f.clone()));
300298
// See if this is an introspection or data field. We don't worry about
301299
// non-existent fields; those will cause an error later when we execute
302300
// the data_set SelectionSet
@@ -336,7 +334,7 @@ pub fn execute_root_selection_set_uncached(
336334
/// Executes the root selection set of a query.
337335
pub async fn execute_root_selection_set<R: Resolver>(
338336
ctx: Arc<ExecutionContext<R>>,
339-
selection_set: Arc<q::SelectionSet>,
337+
selection_set: Arc<a::SelectionSet>,
340338
root_type: Arc<s::ObjectType>,
341339
block_ptr: Option<BlockPtr>,
342340
) -> Arc<QueryResult> {
@@ -480,7 +478,7 @@ pub async fn execute_root_selection_set<R: Resolver>(
480478
/// Allows passing in a parent value during recursive processing of objects and their fields.
481479
fn execute_selection_set<'a>(
482480
ctx: &'a ExecutionContext<impl Resolver>,
483-
selection_sets: impl Iterator<Item = &'a q::SelectionSet>,
481+
selection_sets: impl Iterator<Item = &'a a::SelectionSet>,
484482
object_type: &s::ObjectType,
485483
prefetched_value: Option<r::Value>,
486484
) -> Result<r::Value, Vec<QueryExecutionError>> {
@@ -494,7 +492,7 @@ fn execute_selection_set<'a>(
494492

495493
fn execute_selection_set_to_map<'a>(
496494
ctx: &'a ExecutionContext<impl Resolver>,
497-
selection_sets: impl Iterator<Item = &'a q::SelectionSet>,
495+
selection_sets: impl Iterator<Item = &'a a::SelectionSet>,
498496
object_type: &s::ObjectType,
499497
prefetched_value: Option<r::Value>,
500498
) -> Result<Object, Vec<QueryExecutionError>> {
@@ -574,8 +572,8 @@ fn execute_selection_set_to_map<'a>(
574572
pub fn collect_fields<'a>(
575573
ctx: &'a ExecutionContext<impl Resolver>,
576574
object_type: &s::ObjectType,
577-
selection_sets: impl Iterator<Item = &'a q::SelectionSet>,
578-
) -> IndexMap<&'a str, Vec<&'a q::Field>> {
575+
selection_sets: impl Iterator<Item = &'a a::SelectionSet>,
576+
) -> IndexMap<&'a str, Vec<&'a a::Field>> {
579577
let mut grouped_fields = IndexMap::new();
580578
collect_fields_inner(
581579
ctx,
@@ -590,26 +588,26 @@ pub fn collect_fields<'a>(
590588
pub fn collect_fields_inner<'a>(
591589
ctx: &'a ExecutionContext<impl Resolver>,
592590
object_type: &s::ObjectType,
593-
selection_sets: impl Iterator<Item = &'a q::SelectionSet>,
591+
selection_sets: impl Iterator<Item = &'a a::SelectionSet>,
594592
visited_fragments: &mut HashSet<&'a str>,
595-
output: &mut IndexMap<&'a str, Vec<&'a q::Field>>,
593+
output: &mut IndexMap<&'a str, Vec<&'a a::Field>>,
596594
) {
597595
for selection_set in selection_sets {
598596
// Only consider selections that are not skipped and should be included
599597
let selections = selection_set
600598
.items
601599
.iter()
602-
.filter(|selection| !qast::skip_selection(selection))
603-
.filter(|selection| qast::include_selection(selection));
600+
.filter(|selection| !selection.skip())
601+
.filter(|selection| selection.include());
604602

605603
for selection in selections {
606604
match selection {
607-
q::Selection::Field(ref field) => {
608-
let response_key = qast::get_response_key(field);
605+
a::Selection::Field(ref field) => {
606+
let response_key = field.response_key();
609607
output.entry(response_key).or_default().push(field);
610608
}
611609

612-
q::Selection::FragmentSpread(spread) => {
610+
a::Selection::FragmentSpread(spread) => {
613611
// Only consider the fragment if it hasn't already been included,
614612
// as would be the case if the same fragment spread ...Foo appeared
615613
// twice in the same selection set.
@@ -632,7 +630,7 @@ pub fn collect_fields_inner<'a>(
632630
}
633631
}
634632

635-
q::Selection::InlineFragment(fragment) => {
633+
a::Selection::InlineFragment(fragment) => {
636634
let applies = match &fragment.type_condition {
637635
Some(cond) => does_fragment_type_apply(ctx, object_type, &cond),
638636
None => true,
@@ -657,10 +655,10 @@ pub fn collect_fields_inner<'a>(
657655
fn does_fragment_type_apply(
658656
ctx: &ExecutionContext<impl Resolver>,
659657
object_type: &s::ObjectType,
660-
fragment_type: &q::TypeCondition,
658+
fragment_type: &a::TypeCondition,
661659
) -> bool {
662660
// This is safe to do, as TypeCondition only has a single `On` variant.
663-
let q::TypeCondition::On(ref name) = fragment_type;
661+
let a::TypeCondition::On(ref name) = fragment_type;
664662

665663
// Resolve the type the fragment applies to based on its name
666664
let named_type = ctx.query.schema.document().get_named_type(name);
@@ -689,9 +687,9 @@ fn execute_field(
689687
ctx: &ExecutionContext<impl Resolver>,
690688
object_type: &s::ObjectType,
691689
field_value: Option<r::Value>,
692-
field: &q::Field,
690+
field: &a::Field,
693691
field_definition: &s::Field,
694-
fields: Vec<&q::Field>,
692+
fields: Vec<&a::Field>,
695693
) -> Result<r::Value, Vec<QueryExecutionError>> {
696694
coerce_argument_values(&ctx.query, object_type, field)
697695
.and_then(|argument_values| {
@@ -713,7 +711,7 @@ fn resolve_field_value(
713711
ctx: &ExecutionContext<impl Resolver>,
714712
object_type: &s::ObjectType,
715713
field_value: Option<r::Value>,
716-
field: &q::Field,
714+
field: &a::Field,
717715
field_definition: &s::Field,
718716
field_type: &s::Type,
719717
argument_values: &HashMap<&str, r::Value>,
@@ -756,7 +754,7 @@ fn resolve_field_value_for_named_type(
756754
ctx: &ExecutionContext<impl Resolver>,
757755
object_type: &s::ObjectType,
758756
field_value: Option<r::Value>,
759-
field: &q::Field,
757+
field: &a::Field,
760758
field_definition: &s::Field,
761759
type_name: &str,
762760
argument_values: &HashMap<&str, r::Value>,
@@ -809,7 +807,7 @@ fn resolve_field_value_for_list_type(
809807
ctx: &ExecutionContext<impl Resolver>,
810808
object_type: &s::ObjectType,
811809
field_value: Option<r::Value>,
812-
field: &q::Field,
810+
field: &a::Field,
813811
field_definition: &s::Field,
814812
inner_type: &s::Type,
815813
argument_values: &HashMap<&str, r::Value>,
@@ -890,9 +888,9 @@ fn resolve_field_value_for_list_type(
890888
/// Ensures that a value matches the expected return type.
891889
fn complete_value(
892890
ctx: &ExecutionContext<impl Resolver>,
893-
field: &q::Field,
891+
field: &a::Field,
894892
field_type: &s::Type,
895-
fields: &Vec<&q::Field>,
893+
fields: &Vec<&a::Field>,
896894
resolved_value: r::Value,
897895
) -> Result<r::Value, Vec<QueryExecutionError>> {
898896
match field_type {
@@ -1037,7 +1035,7 @@ fn resolve_abstract_type<'a>(
10371035
pub fn coerce_argument_values<'a>(
10381036
query: &crate::execution::Query,
10391037
ty: impl Into<ObjectOrInterface<'a>>,
1040-
field: &q::Field,
1038+
field: &a::Field,
10411039
) -> Result<HashMap<&'a str, r::Value>, Vec<QueryExecutionError>> {
10421040
let mut coerced_values = HashMap::new();
10431041
let mut errors = vec![];
@@ -1048,7 +1046,7 @@ pub fn coerce_argument_values<'a>(
10481046
.into_iter()
10491047
.flatten()
10501048
{
1051-
let value = qast::get_argument_value(&field.arguments, &argument_def.name).cloned();
1049+
let value = field.argument_value(&argument_def.name).cloned();
10521050
match coercion::coerce_input_value(value, &argument_def, &resolver) {
10531051
Ok(Some(value)) => {
10541052
if argument_def.name == "text".to_string() {

0 commit comments

Comments
 (0)