Skip to content

Commit c5cbf4f

Browse files
committed
graphql: Transform the query before executing it
1 parent 24350c7 commit c5cbf4f

File tree

8 files changed

+606
-900
lines changed

8 files changed

+606
-900
lines changed

graphql/src/execution/ast.rs

Lines changed: 243 additions & 93 deletions
Large diffs are not rendered by default.

graphql/src/execution/execution.rs

Lines changed: 31 additions & 157 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,13 @@ use graph::{
55
prelude::{s, CheapClone},
66
util::timed_rw_lock::TimedMutex,
77
};
8-
use indexmap::IndexMap;
98
use lazy_static::lazy_static;
109
use stable_hash::crypto::SetHasher;
1110
use stable_hash::prelude::*;
1211
use stable_hash::utils::stable_hash;
13-
use std::borrow::ToOwned;
14-
use std::collections::{HashMap, HashSet};
15-
use std::iter;
12+
use std::collections::HashMap;
1613
use std::time::Instant;
14+
use std::{borrow::ToOwned, collections::HashSet};
1715

1816
use graph::data::graphql::*;
1917
use graph::data::query::CacheStatus;
@@ -136,7 +134,6 @@ impl Default for WeightedResult {
136134

137135
struct HashableQuery<'a> {
138136
query_schema_id: &'a DeploymentHash,
139-
query_fragments: &'a HashMap<String, a::FragmentDefinition>,
140137
selection_set: &'a a::SelectionSet,
141138
block_ptr: &'a BlockPtr,
142139
}
@@ -162,13 +159,6 @@ impl StableHash for HashableQuery<'_> {
162159
self.query_schema_id
163160
.stable_hash(sequence_number.next_child(), state);
164161

165-
// Not stable! Uses to_string()
166-
self.query_fragments
167-
.iter()
168-
.map(|(k, v)| (k, format!("{:?}", v)))
169-
.collect::<HashMap<_, _>>()
170-
.stable_hash(sequence_number.next_child(), state);
171-
172162
// Not stable! Uses to_string
173163
format!("{:?}", self.selection_set).stable_hash(sequence_number.next_child(), state);
174164

@@ -187,7 +177,6 @@ fn cache_key(
187177
// Otherwise, incorrect results may be returned.
188178
let query = HashableQuery {
189179
query_schema_id: ctx.query.schema.id(),
190-
query_fragments: &ctx.query.fragments,
191180
selection_set,
192181
block_ptr,
193182
};
@@ -275,7 +264,7 @@ where
275264
}
276265
}
277266

278-
pub fn execute_root_selection_set_uncached(
267+
pub(crate) fn execute_root_selection_set_uncached(
279268
ctx: &ExecutionContext<impl Resolver>,
280269
selection_set: &a::SelectionSet,
281270
root_type: &s::ObjectType,
@@ -286,18 +275,16 @@ pub fn execute_root_selection_set_uncached(
286275
let mut intro_set = a::SelectionSet::empty_from(selection_set);
287276
let mut meta_items = Vec::new();
288277

289-
for (_, fields) in collect_fields(ctx, root_type, iter::once(selection_set)) {
290-
let name = fields[0].name.clone();
291-
let selections = fields.into_iter().map(|f| a::Selection::Field(f.clone()));
278+
for field in selection_set.fields_for(root_type) {
292279
// See if this is an introspection or data field. We don't worry about
293280
// non-existent fields; those will cause an error later when we execute
294281
// the data_set SelectionSet
295-
if is_introspection_field(&name) {
296-
intro_set.extend(selections)
297-
} else if &name == META_FIELD_NAME {
298-
meta_items.extend(selections)
282+
if is_introspection_field(&field.name) {
283+
intro_set.push(field)
284+
} else if &field.name == META_FIELD_NAME {
285+
meta_items.push(field)
299286
} else {
300-
data_set.extend(selections)
287+
data_set.push(field)
301288
}
302289
}
303290

@@ -306,8 +293,8 @@ pub fn execute_root_selection_set_uncached(
306293
Object::default()
307294
} else {
308295
let initial_data = ctx.resolver.prefetch(&ctx, &data_set)?;
309-
data_set.extend(meta_items);
310-
execute_selection_set_to_map(&ctx, iter::once(&data_set), root_type, initial_data)?
296+
data_set.push_fields(meta_items);
297+
execute_selection_set_to_map(&ctx, &data_set, root_type, initial_data)?
311298
};
312299

313300
// Resolve introspection fields, if there are any
@@ -316,7 +303,7 @@ pub fn execute_root_selection_set_uncached(
316303

317304
values.extend(execute_selection_set_to_map(
318305
&ictx,
319-
iter::once(&intro_set),
306+
ctx.query.selection_set.as_ref(),
320307
&*INTROSPECTION_QUERY_TYPE,
321308
None,
322309
)?);
@@ -326,7 +313,7 @@ pub fn execute_root_selection_set_uncached(
326313
}
327314

328315
/// Executes the root selection set of a query.
329-
pub async fn execute_root_selection_set<R: Resolver>(
316+
pub(crate) async fn execute_root_selection_set<R: Resolver>(
330317
ctx: Arc<ExecutionContext<R>>,
331318
selection_set: Arc<a::SelectionSet>,
332319
root_type: Arc<s::ObjectType>,
@@ -472,21 +459,21 @@ pub async fn execute_root_selection_set<R: Resolver>(
472459
/// Allows passing in a parent value during recursive processing of objects and their fields.
473460
fn execute_selection_set<'a>(
474461
ctx: &'a ExecutionContext<impl Resolver>,
475-
selection_sets: impl Iterator<Item = &'a a::SelectionSet>,
462+
selection_set: &'a a::SelectionSet,
476463
object_type: &s::ObjectType,
477464
prefetched_value: Option<r::Value>,
478465
) -> Result<r::Value, Vec<QueryExecutionError>> {
479466
Ok(r::Value::Object(execute_selection_set_to_map(
480467
ctx,
481-
selection_sets,
468+
selection_set,
482469
object_type,
483470
prefetched_value,
484471
)?))
485472
}
486473

487474
fn execute_selection_set_to_map<'a>(
488475
ctx: &'a ExecutionContext<impl Resolver>,
489-
selection_sets: impl Iterator<Item = &'a a::SelectionSet>,
476+
selection_set: &'a a::SelectionSet,
490477
object_type: &s::ObjectType,
491478
prefetched_value: Option<r::Value>,
492479
) -> Result<Object, Vec<QueryExecutionError>> {
@@ -498,14 +485,11 @@ fn execute_selection_set_to_map<'a>(
498485
let mut errors: Vec<QueryExecutionError> = Vec::new();
499486
let mut result_map = Object::new();
500487

501-
// Group fields with the same response key, so we can execute them together
502-
let grouped_field_set = collect_fields(ctx, object_type, selection_sets);
503-
504488
// Gather fields that appear more than once with the same response key.
505489
let multiple_response_keys = {
506490
let mut multiple_response_keys = HashSet::new();
507491
let mut fields = HashSet::new();
508-
for field in grouped_field_set.iter().map(|(_, f)| f.iter()).flatten() {
492+
for field in selection_set.fields_for(object_type) {
509493
if !fields.insert(field.name.as_str()) {
510494
multiple_response_keys.insert(field.name.as_str());
511495
}
@@ -514,7 +498,7 @@ fn execute_selection_set_to_map<'a>(
514498
};
515499

516500
// Process all field groups in order
517-
for (response_key, fields) in grouped_field_set {
501+
for field in selection_set.fields_for(object_type) {
518502
match ctx.deadline {
519503
Some(deadline) if deadline < Instant::now() => {
520504
errors.push(QueryExecutionError::Timeout);
@@ -523,8 +507,10 @@ fn execute_selection_set_to_map<'a>(
523507
_ => (),
524508
}
525509

510+
let response_key = field.response_key();
511+
526512
// Unwrap: The query was validated to contain only valid fields.
527-
let field = sast::get_field(object_type, &fields[0].name).unwrap();
513+
let field_type = sast::get_field(object_type, &field.name).unwrap();
528514

529515
// Check if we have the value already.
530516
let field_value = prefetched_object
@@ -537,13 +523,13 @@ fn execute_selection_set_to_map<'a>(
537523

538524
// Scalars and scalar lists are associated to the field name.
539525
// If the field has more than one response key, we have to clone.
540-
match multiple_response_keys.contains(fields[0].name.as_str()) {
541-
false => o.remove(&fields[0].name),
542-
true => o.get(&fields[0].name).cloned(),
526+
match multiple_response_keys.contains(field.name.as_str()) {
527+
false => o.remove(&field.name),
528+
true => o.get(&field.name).cloned(),
543529
}
544530
})
545531
.flatten();
546-
match execute_field(&ctx, object_type, field_value, &fields[0], field, fields) {
532+
match execute_field(&ctx, object_type, field_value, field, field_type) {
547533
Ok(v) => {
548534
result_map.insert(response_key.to_owned(), v);
549535
}
@@ -560,124 +546,13 @@ fn execute_selection_set_to_map<'a>(
560546
}
561547
}
562548

563-
/// Collects fields from selection sets. Returns a map from response key to fields. There will
564-
/// typically be a single field for a response key. If there are multiple, the overall execution
565-
/// logic will effectively merged them into the output for the response key.
566-
pub fn collect_fields<'a>(
567-
ctx: &'a ExecutionContext<impl Resolver>,
568-
object_type: &s::ObjectType,
569-
selection_sets: impl Iterator<Item = &'a a::SelectionSet>,
570-
) -> IndexMap<&'a str, Vec<&'a a::Field>> {
571-
let mut grouped_fields = IndexMap::new();
572-
collect_fields_inner(
573-
ctx,
574-
object_type,
575-
selection_sets,
576-
&mut HashSet::new(),
577-
&mut grouped_fields,
578-
);
579-
grouped_fields
580-
}
581-
582-
pub fn collect_fields_inner<'a>(
583-
ctx: &'a ExecutionContext<impl Resolver>,
584-
object_type: &s::ObjectType,
585-
selection_sets: impl Iterator<Item = &'a a::SelectionSet>,
586-
visited_fragments: &mut HashSet<&'a str>,
587-
output: &mut IndexMap<&'a str, Vec<&'a a::Field>>,
588-
) {
589-
for selection_set in selection_sets {
590-
// Only consider selections that are not skipped and should be included
591-
for selection in selection_set.included() {
592-
match selection {
593-
a::Selection::Field(ref field) => {
594-
let response_key = field.response_key();
595-
output.entry(response_key).or_default().push(field);
596-
}
597-
598-
a::Selection::FragmentSpread(spread) => {
599-
// Only consider the fragment if it hasn't already been included,
600-
// as would be the case if the same fragment spread ...Foo appeared
601-
// twice in the same selection set.
602-
//
603-
// Note: This will skip both duplicate fragments and will break cycles,
604-
// so we support fragments even though the GraphQL spec prohibits them.
605-
if visited_fragments.insert(&spread.fragment_name) {
606-
let fragment = ctx.query.get_fragment(&spread.fragment_name);
607-
if does_fragment_type_apply(ctx, object_type, &fragment.type_condition) {
608-
// We have a fragment that applies to the current object type,
609-
// collect fields recursively
610-
collect_fields_inner(
611-
ctx,
612-
object_type,
613-
iter::once(&fragment.selection_set),
614-
visited_fragments,
615-
output,
616-
);
617-
}
618-
}
619-
}
620-
621-
a::Selection::InlineFragment(fragment) => {
622-
let applies = match &fragment.type_condition {
623-
Some(cond) => does_fragment_type_apply(ctx, object_type, &cond),
624-
None => true,
625-
};
626-
627-
if applies {
628-
collect_fields_inner(
629-
ctx,
630-
object_type,
631-
iter::once(&fragment.selection_set),
632-
visited_fragments,
633-
output,
634-
)
635-
}
636-
}
637-
};
638-
}
639-
}
640-
}
641-
642-
/// Determines whether a fragment is applicable to the given object type.
643-
fn does_fragment_type_apply(
644-
ctx: &ExecutionContext<impl Resolver>,
645-
object_type: &s::ObjectType,
646-
fragment_type: &a::TypeCondition,
647-
) -> bool {
648-
// This is safe to do, as TypeCondition only has a single `On` variant.
649-
let a::TypeCondition::On(ref name) = fragment_type;
650-
651-
// Resolve the type the fragment applies to based on its name
652-
let named_type = ctx.query.schema.document().get_named_type(name);
653-
654-
match named_type {
655-
// The fragment applies to the object type if its type is the same object type
656-
Some(s::TypeDefinition::Object(ot)) => object_type == ot,
657-
658-
// The fragment also applies to the object type if its type is an interface
659-
// that the object type implements
660-
Some(s::TypeDefinition::Interface(it)) => {
661-
object_type.implements_interfaces.contains(&it.name)
662-
}
663-
664-
// The fragment also applies to an object type if its type is a union that
665-
// the object type is one of the possible types for
666-
Some(s::TypeDefinition::Union(ut)) => ut.types.contains(&object_type.name),
667-
668-
// In all other cases, the fragment does not apply
669-
_ => false,
670-
}
671-
}
672-
673549
/// Executes a field.
674550
fn execute_field(
675551
ctx: &ExecutionContext<impl Resolver>,
676552
object_type: &s::ObjectType,
677553
field_value: Option<r::Value>,
678554
field: &a::Field,
679555
field_definition: &s::Field,
680-
fields: Vec<&a::Field>,
681556
) -> Result<r::Value, Vec<QueryExecutionError>> {
682557
coerce_argument_values(&ctx.query, object_type, field)
683558
.and_then(|argument_values| {
@@ -691,7 +566,7 @@ fn execute_field(
691566
&argument_values,
692567
)
693568
})
694-
.and_then(|value| complete_value(ctx, field, &field_definition.field_type, &fields, value))
569+
.and_then(|value| complete_value(ctx, field, &field_definition.field_type, value))
695570
}
696571

697572
/// Resolves the value of a field.
@@ -878,13 +753,12 @@ fn complete_value(
878753
ctx: &ExecutionContext<impl Resolver>,
879754
field: &a::Field,
880755
field_type: &s::Type,
881-
fields: &Vec<&a::Field>,
882756
resolved_value: r::Value,
883757
) -> Result<r::Value, Vec<QueryExecutionError>> {
884758
match field_type {
885759
// Fail if the field type is non-null but the value is null
886760
s::Type::NonNullType(inner_type) => {
887-
return match complete_value(ctx, field, inner_type, fields, resolved_value)? {
761+
return match complete_value(ctx, field, inner_type, resolved_value)? {
888762
r::Value::Null => Err(vec![QueryExecutionError::NonNullError(
889763
field.position,
890764
field.name.to_string(),
@@ -908,7 +782,7 @@ fn complete_value(
908782
for value_place in &mut values {
909783
// Put in a placeholder, complete the value, put the completed value back.
910784
let value = std::mem::replace(value_place, r::Value::Null);
911-
match complete_value(ctx, field, inner_type, fields, value) {
785+
match complete_value(ctx, field, inner_type, value) {
912786
Ok(value) => {
913787
*value_place = value;
914788
}
@@ -965,7 +839,7 @@ fn complete_value(
965839
// Complete object types recursively
966840
s::TypeDefinition::Object(object_type) => execute_selection_set(
967841
ctx,
968-
fields.iter().map(|f| &f.selection_set),
842+
&field.selection_set,
969843
object_type,
970844
Some(resolved_value),
971845
),
@@ -976,7 +850,7 @@ fn complete_value(
976850

977851
execute_selection_set(
978852
ctx,
979-
fields.iter().map(|f| &f.selection_set),
853+
&field.selection_set,
980854
object_type,
981855
Some(resolved_value),
982856
)
@@ -988,7 +862,7 @@ fn complete_value(
988862

989863
execute_selection_set(
990864
ctx,
991-
fields.iter().map(|f| &f.selection_set),
865+
&field.selection_set,
992866
object_type,
993867
Some(resolved_value),
994868
)

0 commit comments

Comments
 (0)