Skip to content

Commit deaa15f

Browse files
committed
graph, graphql: Use Arc<ObjctType> to reference object types
Rather than use a string name, use the actual object type to identify types. It's not possible to do this with plain references, for example, because we pass a reference to a SelectionSet to graph::spawn_blocking, so we do the next best thing and use an Arc. Unfortunately, because `graphql_parser` doesn't wrap its object types in an Arc, that means we need to keep a copy of all of them in ApiSchema.
1 parent 3bf9aab commit deaa15f

File tree

12 files changed

+165
-124
lines changed

12 files changed

+165
-124
lines changed

graph/src/data/schema.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::cheap_clone::CheapClone;
12
use crate::components::store::{EntityType, SubgraphStore};
23
use crate::data::graphql::ext::{DirectiveExt, DirectiveFinder, DocumentExt, TypeExt, ValueExt};
34
use crate::data::store::ValueType;
@@ -353,6 +354,7 @@ pub struct ApiSchema {
353354
// Root types for the api schema.
354355
pub query_type: Arc<ObjectType>,
355356
pub subscription_type: Option<Arc<ObjectType>>,
357+
object_types: HashMap<String, Arc<ObjectType>>,
356358
}
357359

358360
impl ApiSchema {
@@ -376,10 +378,19 @@ impl ApiSchema {
376378
.cloned()
377379
.map(Arc::new);
378380

381+
let object_types = HashMap::from_iter(
382+
api_schema
383+
.document
384+
.get_object_type_definitions()
385+
.into_iter()
386+
.map(|obj_type| (obj_type.name.clone(), Arc::new(obj_type.clone()))),
387+
);
388+
379389
Ok(Self {
380390
schema: api_schema,
381391
query_type: Arc::new(query_type),
382392
subscription_type,
393+
object_types,
383394
})
384395
}
385396

@@ -403,6 +414,17 @@ impl ApiSchema {
403414
pub fn interfaces_for_type(&self, type_name: &EntityType) -> Option<&Vec<InterfaceType>> {
404415
self.schema.interfaces_for_type(type_name)
405416
}
417+
418+
/// Return an `Arc` around the `ObjectType` from our internal cache
419+
///
420+
/// # Panics
421+
/// If `obj_type` is not part of this schema, this function panics
422+
pub fn object_type(&self, obj_type: &ObjectType) -> Arc<ObjectType> {
423+
self.object_types
424+
.get(&obj_type.name)
425+
.expect("ApiSchema.object_type is only used with existing types")
426+
.cheap_clone()
427+
}
406428
}
407429

408430
fn add_introspection_schema(schema: &mut Document) {

graphql/src/execution/ast.rs

Lines changed: 38 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::{collections::HashSet, ops::Deref};
33
use graph::{
44
components::store::EntityType,
55
data::graphql::{DocumentExt, ObjectOrInterface},
6-
prelude::{anyhow, q, r, s, QueryExecutionError, Schema, ValueMap},
6+
prelude::{anyhow, q, r, s, ApiSchema, QueryExecutionError, ValueMap},
77
};
88
use graphql_parser::Pos;
99

@@ -23,13 +23,16 @@ use crate::schema::ast::ObjectType;
2323
pub struct SelectionSet {
2424
// Map object types to the list of fields that should be selected for
2525
// them
26-
items: Vec<(String, Vec<Field>)>,
26+
items: Vec<(ObjectType, Vec<Field>)>,
2727
}
2828

2929
impl SelectionSet {
3030
/// Create a new `SelectionSet` that can handle the given types
31-
pub fn new(types: Vec<String>) -> Self {
32-
let items = types.into_iter().map(|name| (name, Vec::new())).collect();
31+
pub fn new(types: Vec<ObjectType>) -> Self {
32+
let items = types
33+
.into_iter()
34+
.map(|obj_type| (obj_type, Vec::new()))
35+
.collect();
3336
SelectionSet { items }
3437
}
3538

@@ -76,29 +79,28 @@ impl SelectionSet {
7679
}
7780

7881
/// Iterate over all types and the fields for those types
79-
pub fn fields(&self) -> impl Iterator<Item = (&str, impl Iterator<Item = &Field>)> {
82+
pub fn fields(&self) -> impl Iterator<Item = (&ObjectType, impl Iterator<Item = &Field>)> {
8083
self.items
8184
.iter()
82-
.map(|(name, fields)| (name.as_str(), fields.iter()))
85+
.map(|(obj_type, fields)| (obj_type, fields.iter()))
8386
}
8487

8588
/// Iterate over all types and the fields that are not leaf fields, i.e.
8689
/// whose selection sets are not empty
87-
pub fn interior_fields(&self) -> impl Iterator<Item = (&str, impl Iterator<Item = &Field>)> {
88-
self.items.iter().map(|(name, fields)| {
89-
(
90-
name.as_str(),
91-
fields.iter().filter(|field| !field.is_leaf()),
92-
)
93-
})
90+
pub fn interior_fields(
91+
&self,
92+
) -> impl Iterator<Item = (&ObjectType, impl Iterator<Item = &Field>)> {
93+
self.items
94+
.iter()
95+
.map(|(obj_type, fields)| (obj_type, fields.iter().filter(|field| !field.is_leaf())))
9496
}
9597

9698
/// Iterate over all fields for the given object type
97-
pub fn fields_for(&self, obj_type: &s::ObjectType) -> impl Iterator<Item = &Field> {
99+
pub fn fields_for(&self, obj_type: &ObjectType) -> impl Iterator<Item = &Field> {
98100
let item = self
99101
.items
100102
.iter()
101-
.find(|(name, _)| name == &obj_type.name)
103+
.find(|(our_type, _)| our_type == obj_type)
102104
.expect("there is an entry for the type");
103105
item.1.iter()
104106
}
@@ -254,14 +256,14 @@ impl ValueMap for Field {
254256
/// object types that implement them, and possibly narrowing further when
255257
/// expanding fragments with type conitions
256258
#[derive(Debug, Clone, PartialEq)]
257-
pub enum ObjectTypeSet {
259+
pub(crate) enum ObjectTypeSet {
258260
Any,
259-
Only(HashSet<String>),
261+
Only(HashSet<ObjectType>),
260262
}
261263

262264
impl ObjectTypeSet {
263265
pub fn convert(
264-
schema: &Schema,
266+
schema: &ApiSchema,
265267
type_cond: Option<&q::TypeCondition>,
266268
) -> Result<ObjectTypeSet, QueryExecutionError> {
267269
match type_cond {
@@ -270,64 +272,60 @@ impl ObjectTypeSet {
270272
}
271273
}
272274

273-
pub fn from_name(schema: &Schema, name: &str) -> Result<ObjectTypeSet, QueryExecutionError> {
274-
let set = resolve_object_types(schema, name)?
275-
.into_iter()
276-
.map(|ty| ty.name().to_string())
277-
.collect();
275+
pub fn from_name(schema: &ApiSchema, name: &str) -> Result<ObjectTypeSet, QueryExecutionError> {
276+
let set = resolve_object_types(schema, name)?;
278277
Ok(ObjectTypeSet::Only(set))
279278
}
280279

281-
fn matches_name(&self, name: &str) -> bool {
280+
fn contains(&self, obj_type: &ObjectType) -> bool {
282281
match self {
283282
ObjectTypeSet::Any => true,
284-
ObjectTypeSet::Only(set) => set.contains(name),
283+
ObjectTypeSet::Only(set) => set.contains(obj_type),
285284
}
286285
}
287286

288287
pub fn intersect(self, other: &ObjectTypeSet) -> ObjectTypeSet {
289288
match self {
290289
ObjectTypeSet::Any => other.clone(),
291-
ObjectTypeSet::Only(set) => ObjectTypeSet::Only(
292-
set.into_iter()
293-
.filter(|ty| other.matches_name(ty))
294-
.collect(),
295-
),
290+
ObjectTypeSet::Only(set) => {
291+
ObjectTypeSet::Only(set.into_iter().filter(|ty| other.contains(ty)).collect())
292+
}
296293
}
297294
}
298295

299296
/// Return a list of the object type names that are in this type set and
300297
/// are also implementations of `current_type`
301298
pub fn type_names(
302299
&self,
303-
schema: &Schema,
300+
schema: &ApiSchema,
304301
current_type: ObjectOrInterface<'_>,
305-
) -> Result<Vec<String>, QueryExecutionError> {
302+
) -> Result<Vec<ObjectType>, QueryExecutionError> {
306303
Ok(resolve_object_types(schema, current_type.name())?
307304
.into_iter()
308-
.map(|obj| obj.name().to_string())
309-
.filter(|name| match self {
305+
.filter(|obj_type| match self {
310306
ObjectTypeSet::Any => true,
311-
ObjectTypeSet::Only(set) => set.contains(name.as_str()),
307+
ObjectTypeSet::Only(set) => set.contains(obj_type),
312308
})
313-
.collect::<Vec<String>>())
309+
.collect())
314310
}
315311
}
316312

317313
/// Look up the type `name` from the schema and resolve interfaces
318314
/// and unions until we are left with a set of concrete object types
319-
pub(crate) fn resolve_object_types<'a>(
320-
schema: &'a Schema,
315+
pub(crate) fn resolve_object_types(
316+
schema: &ApiSchema,
321317
name: &str,
322-
) -> Result<HashSet<ObjectType<'a>>, QueryExecutionError> {
318+
) -> Result<HashSet<ObjectType>, QueryExecutionError> {
323319
let mut set = HashSet::new();
324320
match schema
321+
.schema
325322
.document
326323
.get_named_type(name)
327324
.ok_or_else(|| QueryExecutionError::AbstractTypeError(name.to_string()))?
328325
{
329326
s::TypeDefinition::Interface(intf) => {
330327
for obj_ty in &schema.types_for_interface()[&EntityType::new(intf.name.to_string())] {
328+
let obj_ty = schema.object_type(obj_ty);
331329
set.insert(obj_ty.into());
332330
}
333331
}
@@ -337,6 +335,7 @@ pub(crate) fn resolve_object_types<'a>(
337335
}
338336
}
339337
s::TypeDefinition::Object(ty) => {
338+
let ty = schema.object_type(ty);
340339
set.insert(ty.into());
341340
}
342341
s::TypeDefinition::Scalar(_)

graphql/src/execution/execution.rs

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ pub(crate) fn get_field<'a>(
222222
name: &str,
223223
) -> Option<s::Field> {
224224
if name == "__schema" || name == "__type" {
225-
let object_type = *INTROSPECTION_QUERY_TYPE;
225+
let object_type = &*INTROSPECTION_QUERY_TYPE;
226226
sast::get_field(object_type, name).cloned()
227227
} else {
228228
sast::get_field(object_type, name).cloned()
@@ -265,7 +265,7 @@ where
265265
pub(crate) fn execute_root_selection_set_uncached(
266266
ctx: &ExecutionContext<impl Resolver>,
267267
selection_set: &a::SelectionSet,
268-
root_type: &s::ObjectType,
268+
root_type: &sast::ObjectType,
269269
) -> Result<Object, Vec<QueryExecutionError>> {
270270
// Split the top-level fields into introspection fields and
271271
// regular data fields
@@ -314,7 +314,7 @@ pub(crate) fn execute_root_selection_set_uncached(
314314
pub(crate) async fn execute_root_selection_set<R: Resolver>(
315315
ctx: Arc<ExecutionContext<R>>,
316316
selection_set: Arc<a::SelectionSet>,
317-
root_type: Arc<s::ObjectType>,
317+
root_type: sast::ObjectType,
318318
block_ptr: Option<BlockPtr>,
319319
) -> Arc<QueryResult> {
320320
// Cache the cache key to not have to calculate it twice - once for lookup
@@ -458,7 +458,7 @@ pub(crate) async fn execute_root_selection_set<R: Resolver>(
458458
fn execute_selection_set<'a>(
459459
ctx: &'a ExecutionContext<impl Resolver>,
460460
selection_set: &'a a::SelectionSet,
461-
object_type: &s::ObjectType,
461+
object_type: &sast::ObjectType,
462462
prefetched_value: Option<r::Value>,
463463
) -> Result<r::Value, Vec<QueryExecutionError>> {
464464
Ok(r::Value::Object(execute_selection_set_to_map(
@@ -472,7 +472,7 @@ fn execute_selection_set<'a>(
472472
fn execute_selection_set_to_map<'a>(
473473
ctx: &'a ExecutionContext<impl Resolver>,
474474
selection_set: &'a a::SelectionSet,
475-
object_type: &s::ObjectType,
475+
object_type: &sast::ObjectType,
476476
prefetched_value: Option<r::Value>,
477477
) -> Result<Object, Vec<QueryExecutionError>> {
478478
let mut prefetched_object = match prefetched_value {
@@ -806,12 +806,15 @@ fn complete_value(
806806
}
807807

808808
// Complete object types recursively
809-
s::TypeDefinition::Object(object_type) => execute_selection_set(
810-
ctx,
811-
&field.selection_set,
812-
object_type,
813-
Some(resolved_value),
814-
),
809+
s::TypeDefinition::Object(object_type) => {
810+
let object_type = ctx.query.schema.object_type(object_type).into();
811+
execute_selection_set(
812+
ctx,
813+
&field.selection_set,
814+
&object_type,
815+
Some(resolved_value),
816+
)
817+
}
815818

816819
// Resolve interface types using the resolved value and complete the value recursively
817820
s::TypeDefinition::Interface(_) => {
@@ -820,7 +823,7 @@ fn complete_value(
820823
execute_selection_set(
821824
ctx,
822825
&field.selection_set,
823-
object_type,
826+
&object_type,
824827
Some(resolved_value),
825828
)
826829
}
@@ -832,7 +835,7 @@ fn complete_value(
832835
execute_selection_set(
833836
ctx,
834837
&field.selection_set,
835-
object_type,
838+
&object_type,
836839
Some(resolved_value),
837840
)
838841
}
@@ -850,14 +853,16 @@ fn resolve_abstract_type<'a>(
850853
ctx: &'a ExecutionContext<impl Resolver>,
851854
abstract_type: &s::TypeDefinition,
852855
object_value: &r::Value,
853-
) -> Result<&'a s::ObjectType, Vec<QueryExecutionError>> {
856+
) -> Result<sast::ObjectType, Vec<QueryExecutionError>> {
854857
// Let the resolver handle the type resolution, return an error if the resolution
855858
// yields nothing
856-
ctx.resolver
859+
let obj_type = ctx
860+
.resolver
857861
.resolve_abstract_type(ctx.query.schema.document(), abstract_type, object_value)
858862
.ok_or_else(|| {
859863
vec![QueryExecutionError::AbstractTypeError(
860864
sast::get_type_name(abstract_type).to_string(),
861865
)]
862-
})
866+
})?;
867+
Ok(ctx.query.schema.object_type(obj_type).into())
863868
}

graphql/src/execution/query.rs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,8 @@ struct SelectedFields<'a>(&'a a::SelectionSet);
8888
impl<'a> std::fmt::Display for SelectedFields<'a> {
8989
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
9090
let mut first = true;
91-
for (name, fields) in self.0.fields() {
92-
write!(fmt, "{}:", name)?;
91+
for (obj_type, fields) in self.0.fields() {
92+
write!(fmt, "{}:", obj_type.name)?;
9393
for field in fields {
9494
if first {
9595
write!(fmt, "{}", field.response_key())?;
@@ -259,9 +259,9 @@ impl Query {
259259
{
260260
let mut bcs: Vec<(BlockConstraint, (a::SelectionSet, ErrorPolicy))> = Vec::new();
261261

262-
let root_type = self.schema.query_type.as_ref();
262+
let root_type = sast::ObjectType::from(self.schema.query_type.cheap_clone());
263263
let mut prev_bc: Option<BlockConstraint> = None;
264-
for field in self.selection_set.fields_for(root_type) {
264+
for field in self.selection_set.fields_for(&root_type) {
265265
let bc = match field.argument_value("block") {
266266
Some(bc) => BlockConstraint::try_from_value(bc).map_err(|_| {
267267
vec![QueryExecutionError::InvalidArgumentError(
@@ -863,7 +863,7 @@ impl Transform {
863863
} else {
864864
let field_type = parent_type.field(&name).expect("field names are valid");
865865
let ty = field_type.field_type.get_base_type();
866-
let type_set = a::ObjectTypeSet::from_name(&self.schema.schema, ty)?;
866+
let type_set = a::ObjectTypeSet::from_name(&self.schema, ty)?;
867867
let ty = self.schema.document().object_or_interface(ty).unwrap();
868868
self.expand_selection_set(selection_set, &type_set, ty)?
869869
};
@@ -893,7 +893,7 @@ impl Transform {
893893
let mut visited_fragments = HashSet::new();
894894

895895
// All the types that could possibly be returned by this selection set
896-
let types = type_set.type_names(&self.schema.schema, ty)?;
896+
let types = type_set.type_names(&self.schema, ty)?;
897897
let mut newset = a::SelectionSet::new(types);
898898

899899
for sel in items {
@@ -972,8 +972,7 @@ impl Transform {
972972
None => ty,
973973
};
974974
if !skip {
975-
let type_set =
976-
a::ObjectTypeSet::convert(&self.schema.schema, frag_cond)?.intersect(type_set);
975+
let type_set = a::ObjectTypeSet::convert(&self.schema, frag_cond)?.intersect(type_set);
977976
let selection_set = self.expand_selection_set(selection_set, &type_set, ty)?;
978977
newset.merge(selection_set, directives);
979978
}

graphql/src/execution/resolver.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use graph::components::store::UnitStream;
2-
use graph::prelude::{async_trait, s, tokio, Error, QueryExecutionError};
2+
use graph::prelude::{async_trait, s, tokio, ApiSchema, Error, QueryExecutionError};
33
use graph::{
44
data::graphql::{ext::DocumentExt, ObjectOrInterface},
55
prelude::{r, QueryResult},
@@ -108,7 +108,7 @@ pub trait Resolver: Sized + Send + Sync + 'static {
108108
// Resolves a change stream for a given field.
109109
fn resolve_field_stream(
110110
&self,
111-
_schema: &s::Document,
111+
_schema: &ApiSchema,
112112
_object_type: &s::ObjectType,
113113
_field: &a::Field,
114114
) -> Result<UnitStream, QueryExecutionError> {

0 commit comments

Comments
 (0)