graphgate_planner/
builder.rs

1#![allow(clippy::too_many_arguments)]
2
3use std::collections::HashMap;
4
5use graphgate_schema::{ComposedSchema, KeyFields, MetaField, MetaType, TypeKind, ValueExt};
6use indexmap::IndexMap;
7use parser::types::{
8    BaseType, DocumentOperations, ExecutableDocument, Field, FragmentDefinition,
9    OperationDefinition, OperationType, Selection, SelectionSet, Type, VariableDefinition,
10};
11use parser::Positioned;
12use value::{ConstValue, Name, Value, Variables};
13
14use crate::plan::{
15    FetchNode, FlattenNode, IntrospectionDirective, IntrospectionField, IntrospectionNode,
16    IntrospectionSelectionSet, ParallelNode, PathSegment, PlanNode, ResponsePath, SequenceNode,
17};
18use crate::types::{
19    FetchEntity, FetchEntityGroup, FetchEntityKey, FetchQuery, FieldRef, MutationRootGroup,
20    QueryRootGroup, RequiredRef, RootGroup, SelectionRef, SelectionRefSet, VariableDefinitionsRef,
21    VariablesRef,
22};
23use crate::{Response, RootNode, ServerError, SubscribeNode};
24
25struct Context<'a> {
26    schema: &'a ComposedSchema,
27    fragments: &'a HashMap<Name, Positioned<FragmentDefinition>>,
28    variables: &'a Variables,
29    key_id: usize,
30}
31
32/// Query plan generator
33pub struct PlanBuilder<'a> {
34    schema: &'a ComposedSchema,
35    document: ExecutableDocument,
36    operation_name: Option<String>,
37    variables: Variables,
38}
39
40impl<'a> PlanBuilder<'a> {
41    pub fn new(schema: &'a ComposedSchema, document: ExecutableDocument) -> Self {
42        Self {
43            schema,
44            document,
45            operation_name: None,
46            variables: Default::default(),
47        }
48    }
49
50    pub fn operation_name(mut self, operation: impl Into<String>) -> Self {
51        self.operation_name = Some(operation.into());
52        self
53    }
54
55    pub fn variables(self, variables: Variables) -> Self {
56        Self { variables, ..self }
57    }
58
59    fn check_rules(&self) -> Result<(), Response> {
60        let rule_errors =
61            graphgate_validation::check_rules(self.schema, &self.document, &self.variables);
62        if !rule_errors.is_empty() {
63            return Err(Response {
64                data: ConstValue::Null,
65                errors: rule_errors
66                    .into_iter()
67                    .map(|err| ServerError {
68                        message: err.message,
69                        locations: err.locations,
70                    })
71                    .collect(),
72                extensions: Default::default(),
73            });
74        }
75        Ok(())
76    }
77
78    fn create_context(&self) -> Context<'_> {
79        let fragments = &self.document.fragments;
80        Context {
81            schema: self.schema,
82            fragments,
83            variables: &self.variables,
84            key_id: 1,
85        }
86    }
87
88    pub fn plan(&self) -> Result<RootNode, Response> {
89        self.check_rules()?;
90
91        let mut ctx = self.create_context();
92        let operation_definition = get_operation(&self.document, self.operation_name.as_deref());
93
94        let root_type = match operation_definition.node.ty {
95            OperationType::Query => ctx.schema.query_type(),
96            OperationType::Mutation => ctx
97                .schema
98                .mutation_type()
99                .expect("The query validator should find this error."),
100            OperationType::Subscription => ctx
101                .schema
102                .subscription_type()
103                .expect("The query validator should find this error."),
104        };
105
106        if let Some(root_type) = ctx.schema.types.get(root_type) {
107            match operation_definition.node.ty {
108                OperationType::Query => Ok(RootNode::Query(ctx.build_root_selection_set(
109                    QueryRootGroup::default(),
110                    operation_definition.node.ty,
111                    &operation_definition.node.variable_definitions,
112                    root_type,
113                    &operation_definition.node.selection_set.node,
114                ))),
115                OperationType::Mutation => Ok(RootNode::Query(ctx.build_root_selection_set(
116                    MutationRootGroup::default(),
117                    operation_definition.node.ty,
118                    &operation_definition.node.variable_definitions,
119                    root_type,
120                    &operation_definition.node.selection_set.node,
121                ))),
122                OperationType::Subscription => Ok(RootNode::Subscribe(ctx.build_subscribe(
123                    &operation_definition.node.variable_definitions,
124                    root_type,
125                    &operation_definition.node.selection_set.node,
126                ))),
127            }
128        } else {
129            unreachable!("The query validator should find this error.")
130        }
131    }
132}
133
134impl<'a> Context<'a> {
135    fn build_root_selection_set(
136        &mut self,
137        mut root_group: impl RootGroup<'a>,
138        operation_type: OperationType,
139        variable_definitions: &'a [Positioned<VariableDefinition>],
140        parent_type: &'a MetaType,
141        selection_set: &'a SelectionSet,
142    ) -> PlanNode<'a> {
143        fn build_root_selection_set_rec<'a>(
144            ctx: &mut Context<'a>,
145            root_group: &mut impl RootGroup<'a>,
146            fetch_entity_group: &mut FetchEntityGroup<'a>,
147            inspection_selection_set: &mut IntrospectionSelectionSet,
148            parent_type: &'a MetaType,
149            selection_set: &'a SelectionSet,
150        ) {
151            for selection in &selection_set.items {
152                match &selection.node {
153                    Selection::Field(field) => {
154                        let field_name = field.node.name.node.as_str();
155                        let field_definition = match parent_type.fields.get(field_name) {
156                            Some(field_definition) => field_definition,
157                            None => continue,
158                        };
159                        if is_introspection_field(field_name) {
160                            ctx.build_introspection_field(inspection_selection_set, &field.node);
161                            continue;
162                        }
163
164                        if let Some(service) = &field_definition.service {
165                            let selection_ref_set = root_group.selection_set_mut(service);
166                            let mut path = ResponsePath::default();
167                            ctx.build_field(
168                                &mut path,
169                                selection_ref_set,
170                                fetch_entity_group,
171                                service,
172                                parent_type,
173                                &field.node,
174                            );
175                        }
176                    }
177                    Selection::FragmentSpread(fragment_spread) => {
178                        if let Some(fragment) = ctx
179                            .fragments
180                            .get(fragment_spread.node.fragment_name.node.as_str())
181                        {
182                            build_root_selection_set_rec(
183                                ctx,
184                                root_group,
185                                fetch_entity_group,
186                                inspection_selection_set,
187                                parent_type,
188                                &fragment.node.selection_set.node,
189                            );
190                        }
191                    }
192                    Selection::InlineFragment(inline_fragment) => {
193                        build_root_selection_set_rec(
194                            ctx,
195                            root_group,
196                            fetch_entity_group,
197                            inspection_selection_set,
198                            parent_type,
199                            &inline_fragment.node.selection_set.node,
200                        );
201                    }
202                }
203            }
204        }
205
206        let mut fetch_entity_group = FetchEntityGroup::default();
207        let mut inspection_selection_set = IntrospectionSelectionSet::default();
208        build_root_selection_set_rec(
209            self,
210            &mut root_group,
211            &mut fetch_entity_group,
212            &mut inspection_selection_set,
213            parent_type,
214            selection_set,
215        );
216
217        let mut nodes = Vec::new();
218        if !inspection_selection_set.0.is_empty() {
219            nodes.push(PlanNode::Introspection(IntrospectionNode {
220                selection_set: inspection_selection_set,
221            }));
222        }
223
224        let fetch_node = {
225            let mut nodes = Vec::new();
226            for (service, selection_set) in root_group.into_selection_set() {
227                let (variables, variable_definitions) =
228                    referenced_variables(&selection_set, self.variables, variable_definitions);
229                nodes.push(PlanNode::Fetch(FetchNode {
230                    service,
231                    variables,
232                    query: FetchQuery {
233                        entity_type: None,
234                        operation_type,
235                        variable_definitions,
236                        selection_set,
237                    },
238                }));
239            }
240            if operation_type == OperationType::Query {
241                PlanNode::Parallel(ParallelNode { nodes }).flatten()
242            } else {
243                PlanNode::Sequence(SequenceNode { nodes }).flatten()
244            }
245        };
246        nodes.push(fetch_node);
247
248        while !fetch_entity_group.is_empty() {
249            let mut flatten_nodes = Vec::new();
250            let mut next_group = FetchEntityGroup::new();
251
252            for (
253                FetchEntityKey {
254                    service, mut path, ..
255                },
256                FetchEntity {
257                    parent_type,
258                    prefix,
259                    fields,
260                },
261            ) in fetch_entity_group
262            {
263                let mut selection_ref_set = SelectionRefSet::default();
264
265                for field in fields {
266                    self.build_field(
267                        &mut path,
268                        &mut selection_ref_set,
269                        &mut next_group,
270                        service,
271                        parent_type,
272                        field,
273                    );
274                }
275
276                let (variables, variable_definitions) =
277                    referenced_variables(&selection_ref_set, self.variables, variable_definitions);
278                flatten_nodes.push(PlanNode::Flatten(FlattenNode {
279                    path,
280                    prefix,
281                    service,
282                    variables,
283                    query: FetchQuery {
284                        entity_type: Some(parent_type.name.as_str()),
285                        operation_type: OperationType::Subscription,
286                        variable_definitions,
287                        selection_set: selection_ref_set,
288                    },
289                }));
290            }
291
292            nodes.push(
293                PlanNode::Parallel(ParallelNode {
294                    nodes: flatten_nodes,
295                })
296                .flatten(),
297            );
298            fetch_entity_group = next_group;
299        }
300
301        PlanNode::Sequence(SequenceNode { nodes }).flatten()
302    }
303
304    fn build_subscribe(
305        &mut self,
306        variable_definitions: &'a [Positioned<VariableDefinition>],
307        parent_type: &'a MetaType,
308        selection_set: &'a SelectionSet,
309    ) -> SubscribeNode<'a> {
310        let mut root_group = QueryRootGroup::default();
311        let mut fetch_entity_group = FetchEntityGroup::default();
312
313        for selection in &selection_set.items {
314            if let Selection::Field(field) = &selection.node {
315                let field_name = field.node.name.node.as_str();
316                let field_definition = match parent_type.fields.get(field_name) {
317                    Some(field_definition) => field_definition,
318                    None => continue,
319                };
320
321                if let Some(service) = &field_definition.service {
322                    let selection_ref_set = root_group.selection_set_mut(service);
323                    let mut path = ResponsePath::default();
324                    self.build_field(
325                        &mut path,
326                        selection_ref_set,
327                        &mut fetch_entity_group,
328                        service,
329                        parent_type,
330                        &field.node,
331                    );
332                }
333            }
334        }
335
336        let fetch_nodes = {
337            let mut nodes = Vec::new();
338            for (service, selection_ref_set) in root_group.into_selection_set() {
339                let (variables, variable_definitions) =
340                    referenced_variables(&selection_ref_set, self.variables, variable_definitions);
341                nodes.push(FetchNode {
342                    service,
343                    variables,
344                    query: FetchQuery {
345                        entity_type: None,
346                        operation_type: OperationType::Subscription,
347                        variable_definitions,
348                        selection_set: selection_ref_set,
349                    },
350                });
351            }
352            nodes
353        };
354
355        let mut query_nodes = Vec::new();
356        while !fetch_entity_group.is_empty() {
357            let mut flatten_nodes = Vec::new();
358            let mut next_group = FetchEntityGroup::new();
359
360            for (
361                FetchEntityKey {
362                    service, mut path, ..
363                },
364                FetchEntity {
365                    parent_type,
366                    prefix,
367                    fields,
368                },
369            ) in fetch_entity_group
370            {
371                let mut selection_ref_set = SelectionRefSet::default();
372
373                for field in fields {
374                    self.build_field(
375                        &mut path,
376                        &mut selection_ref_set,
377                        &mut next_group,
378                        service,
379                        parent_type,
380                        field,
381                    );
382                }
383
384                let (variables, variable_definitions) =
385                    referenced_variables(&selection_ref_set, self.variables, variable_definitions);
386                flatten_nodes.push(PlanNode::Flatten(FlattenNode {
387                    path,
388                    prefix,
389                    service,
390                    variables,
391                    query: FetchQuery {
392                        entity_type: Some(parent_type.name.as_str()),
393                        operation_type: OperationType::Query,
394                        variable_definitions,
395                        selection_set: selection_ref_set,
396                    },
397                }));
398            }
399
400            query_nodes.push(
401                PlanNode::Parallel(ParallelNode {
402                    nodes: flatten_nodes,
403                })
404                .flatten(),
405            );
406            fetch_entity_group = next_group;
407        }
408
409        SubscribeNode {
410            subscribe_nodes: fetch_nodes,
411            flatten_node: if query_nodes.is_empty() {
412                None
413            } else {
414                Some(PlanNode::Sequence(SequenceNode { nodes: query_nodes }).flatten())
415            },
416        }
417    }
418
419    fn build_introspection_field(
420        &mut self,
421        introspection_selection_set: &mut IntrospectionSelectionSet,
422        field: &'a Field,
423    ) {
424        fn build_selection_set<'a>(
425            ctx: &mut Context<'a>,
426            introspection_selection_set: &mut IntrospectionSelectionSet,
427            selection_set: &'a SelectionSet,
428        ) {
429            for selection in &selection_set.items {
430                match &selection.node {
431                    Selection::Field(field) => {
432                        ctx.build_introspection_field(introspection_selection_set, &field.node);
433                    }
434                    Selection::FragmentSpread(fragment_spread) => {
435                        if let Some(fragment) = ctx
436                            .fragments
437                            .get(fragment_spread.node.fragment_name.node.as_str())
438                        {
439                            build_selection_set(
440                                ctx,
441                                introspection_selection_set,
442                                &fragment.node.selection_set.node,
443                            );
444                        }
445                    }
446                    Selection::InlineFragment(inline_fragment) => {
447                        build_selection_set(
448                            ctx,
449                            introspection_selection_set,
450                            &inline_fragment.node.selection_set.node,
451                        );
452                    }
453                }
454            }
455        }
456
457        fn convert_arguments(
458            ctx: &mut Context,
459            arguments: &[(Positioned<Name>, Positioned<Value>)],
460        ) -> IndexMap<Name, ConstValue> {
461            arguments
462                .iter()
463                .map(|(name, value)| {
464                    (
465                        name.node.clone(),
466                        value
467                            .node
468                            .clone()
469                            .into_const_with(|name| {
470                                Ok::<_, std::convert::Infallible>(
471                                    ctx.variables.get(&name).unwrap().clone(),
472                                )
473                            })
474                            .unwrap(),
475                    )
476                })
477                .collect()
478        }
479
480        let mut sub_selection_set = IntrospectionSelectionSet::default();
481        build_selection_set(self, &mut sub_selection_set, &field.selection_set.node);
482        introspection_selection_set.0.push(IntrospectionField {
483            name: field.name.node.clone(),
484            alias: field.alias.clone().map(|alias| alias.node),
485            arguments: convert_arguments(self, &field.arguments),
486            directives: field
487                .directives
488                .iter()
489                .map(|directive| IntrospectionDirective {
490                    name: directive.node.name.node.clone(),
491                    arguments: convert_arguments(self, &directive.node.arguments),
492                })
493                .collect(),
494            selection_set: sub_selection_set,
495        });
496    }
497
498    fn build_field(
499        &mut self,
500        path: &mut ResponsePath<'a>,
501        selection_ref_set: &mut SelectionRefSet<'a>,
502        fetch_entity_group: &mut FetchEntityGroup<'a>,
503        current_service: &'a str,
504        parent_type: &'a MetaType,
505        field: &'a Field,
506    ) {
507        let field_name = field.name.node.as_str();
508
509        if field_name == "__typename" {
510            selection_ref_set
511                .0
512                .push(SelectionRef::IntrospectionTypename);
513            return;
514        }
515
516        let field_definition = match parent_type.fields.get(field_name) {
517            Some(field_definition) => field_definition,
518            None => return,
519        };
520        let field_type = match self.schema.get_type(&field_definition.ty) {
521            Some(field_type) => field_type,
522            None => return,
523        };
524
525        let service = match field_definition
526            .service
527            .as_deref()
528            .or_else(|| parent_type.owner.as_deref())
529        {
530            Some(service) => service,
531            None => current_service,
532        };
533
534        if service != current_service {
535            let mut keys = parent_type.keys.get(service).and_then(|x| x.get(0));
536            if keys.is_none() {
537                if let Some(owner) = &parent_type.owner {
538                    keys = parent_type.keys.get(owner).and_then(|x| x.get(0));
539                }
540            }
541            let keys = match keys {
542                Some(keys) => keys,
543                None => return,
544            };
545            if !self.field_in_keys(field, keys) {
546                self.add_fetch_entity(
547                    path,
548                    selection_ref_set,
549                    fetch_entity_group,
550                    parent_type,
551                    field,
552                    &field_definition,
553                    service,
554                    keys,
555                );
556                return;
557            }
558        }
559
560        path.push(PathSegment {
561            name: field.response_key().node.as_str(),
562            is_list: is_list(&field_definition.ty),
563            possible_type: None,
564        });
565        let mut sub_selection_set = SelectionRefSet::default();
566
567        if matches!(field_type.kind, TypeKind::Interface | TypeKind::Union) {
568            self.build_abstract_selection_set(
569                path,
570                &mut sub_selection_set,
571                fetch_entity_group,
572                current_service,
573                &field_type,
574                &field.selection_set.node,
575            );
576        } else {
577            self.build_selection_set(
578                path,
579                &mut sub_selection_set,
580                fetch_entity_group,
581                current_service,
582                field_type,
583                &field.selection_set.node,
584            );
585        }
586
587        selection_ref_set.0.push(SelectionRef::FieldRef(FieldRef {
588            field,
589            selection_set: sub_selection_set,
590        }));
591        path.pop();
592    }
593
594    fn add_fetch_entity(
595        &mut self,
596        path: &mut ResponsePath<'a>,
597        selection_ref_set: &mut SelectionRefSet<'a>,
598        fetch_entity_group: &mut FetchEntityGroup<'a>,
599        parent_type: &'a MetaType,
600        field: &'a Field,
601        meta_field: &'a MetaField,
602        service: &'a str,
603        keys: &'a KeyFields,
604    ) {
605        let fetch_entity_key = FetchEntityKey {
606            service,
607            path: path.clone(),
608            ty: parent_type.name.as_str(),
609        };
610
611        match fetch_entity_group.get_mut(&fetch_entity_key) {
612            Some(fetch_entity) => {
613                fetch_entity.fields.push(field);
614            }
615            None => {
616                let prefix = self.take_key_prefix();
617                selection_ref_set
618                    .0
619                    .push(SelectionRef::RequiredRef(RequiredRef {
620                        prefix,
621                        fields: keys,
622                        requires: meta_field.requires.as_ref(),
623                    }));
624                fetch_entity_group.insert(
625                    fetch_entity_key,
626                    FetchEntity {
627                        parent_type,
628                        prefix,
629                        fields: vec![field],
630                    },
631                );
632            }
633        }
634    }
635
636    fn build_selection_set(
637        &mut self,
638        path: &mut ResponsePath<'a>,
639        selection_ref_set: &mut SelectionRefSet<'a>,
640        fetch_entity_group: &mut FetchEntityGroup<'a>,
641        current_service: &'a str,
642        parent_type: &'a MetaType,
643        selection_set: &'a SelectionSet,
644    ) {
645        for selection in &selection_set.items {
646            match &selection.node {
647                Selection::Field(field) => {
648                    self.build_field(
649                        path,
650                        selection_ref_set,
651                        fetch_entity_group,
652                        current_service,
653                        parent_type,
654                        &field.node,
655                    );
656                }
657                Selection::FragmentSpread(fragment_spread) => {
658                    if let Some(fragment) = self
659                        .fragments
660                        .get(fragment_spread.node.fragment_name.node.as_str())
661                    {
662                        self.build_selection_set(
663                            path,
664                            selection_ref_set,
665                            fetch_entity_group,
666                            current_service,
667                            parent_type,
668                            &fragment.node.selection_set.node,
669                        );
670                    }
671                }
672                Selection::InlineFragment(inline_fragment) => {
673                    self.build_selection_set(
674                        path,
675                        selection_ref_set,
676                        fetch_entity_group,
677                        current_service,
678                        parent_type,
679                        &inline_fragment.node.selection_set.node,
680                    );
681                }
682            }
683        }
684    }
685
686    fn build_abstract_selection_set(
687        &mut self,
688        path: &mut ResponsePath<'a>,
689        selection_ref_set: &mut SelectionRefSet<'a>,
690        fetch_entity_group: &mut FetchEntityGroup<'a>,
691        current_service: &'a str,
692        parent_type: &'a MetaType,
693        selection_set: &'a SelectionSet,
694    ) {
695        fn build_fields<'a>(
696            ctx: &mut Context<'a>,
697            path: &mut ResponsePath<'a>,
698            selection_ref_set_group: &mut IndexMap<&'a str, SelectionRefSet<'a>>,
699            fetch_entity_group: &mut FetchEntityGroup<'a>,
700            current_service: &'a str,
701            selection_set: &'a SelectionSet,
702            possible_type: &'a MetaType,
703        ) {
704            let current_ty = possible_type.name.as_str();
705
706            for selection in &selection_set.items {
707                match &selection.node {
708                    Selection::Field(field) => {
709                        ctx.build_field(
710                            path,
711                            selection_ref_set_group.entry(current_ty).or_default(),
712                            fetch_entity_group,
713                            current_service,
714                            possible_type,
715                            &field.node,
716                        );
717                    }
718                    Selection::FragmentSpread(fragment_spread) => {
719                        if let Some(fragment) =
720                            ctx.fragments.get(&fragment_spread.node.fragment_name.node)
721                        {
722                            if fragment.node.type_condition.node.on.node == current_ty {
723                                build_fields(
724                                    ctx,
725                                    path,
726                                    selection_ref_set_group,
727                                    fetch_entity_group,
728                                    current_service,
729                                    &fragment.node.selection_set.node,
730                                    possible_type,
731                                );
732                            }
733                        }
734                    }
735                    Selection::InlineFragment(inline_fragment) => {
736                        match inline_fragment
737                            .node
738                            .type_condition
739                            .as_ref()
740                            .map(|node| &node.node)
741                        {
742                            Some(type_condition) if type_condition.on.node == current_ty => {
743                                build_fields(
744                                    ctx,
745                                    path,
746                                    selection_ref_set_group,
747                                    fetch_entity_group,
748                                    current_service,
749                                    &inline_fragment.node.selection_set.node,
750                                    possible_type,
751                                );
752                            }
753                            Some(_type_condition) => {
754                                // Other type condition
755                            }
756                            None => {
757                                build_fields(
758                                    ctx,
759                                    path,
760                                    selection_ref_set_group,
761                                    fetch_entity_group,
762                                    current_service,
763                                    &inline_fragment.node.selection_set.node,
764                                    possible_type,
765                                );
766                            }
767                        }
768                    }
769                }
770            }
771        }
772
773        let mut selection_ref_set_group = IndexMap::new();
774        for possible_type in &parent_type.possible_types {
775            if let Some(ty) = self.schema.types.get(possible_type) {
776                path.last_mut().unwrap().possible_type = Some(ty.name.as_str());
777                build_fields(
778                    self,
779                    path,
780                    &mut selection_ref_set_group,
781                    fetch_entity_group,
782                    current_service,
783                    selection_set,
784                    ty,
785                );
786                path.last_mut().unwrap().possible_type = None;
787            }
788        }
789
790        for (ty, sub_selection_ref_set) in selection_ref_set_group
791            .into_iter()
792            .filter(|(_, selection_ref_set)| !selection_ref_set.0.is_empty())
793        {
794            selection_ref_set.0.push(SelectionRef::InlineFragment {
795                type_condition: Some(ty),
796                selection_set: sub_selection_ref_set,
797            });
798        }
799    }
800
801    fn take_key_prefix(&mut self) -> usize {
802        let id = self.key_id;
803        self.key_id += 1;
804        id
805    }
806
807    fn field_in_keys(&self, field: &Field, keys: &KeyFields) -> bool {
808        fn selection_set_in_keys(
809            ctx: &Context<'_>,
810            selection_set: &SelectionSet,
811            keys: &KeyFields,
812        ) -> bool {
813            for selection in &selection_set.items {
814                match &selection.node {
815                    Selection::Field(field) => {
816                        if !ctx.field_in_keys(&field.node, keys) {
817                            return false;
818                        }
819                    }
820                    Selection::FragmentSpread(fragment_spread) => {
821                        if let Some(fragment) = ctx
822                            .fragments
823                            .get(fragment_spread.node.fragment_name.node.as_str())
824                        {
825                            if !selection_set_in_keys(ctx, &fragment.node.selection_set.node, keys)
826                            {
827                                return false;
828                            }
829                        } else {
830                            return false;
831                        }
832                    }
833                    Selection::InlineFragment(inline_fragment) => {
834                        if !selection_set_in_keys(
835                            ctx,
836                            &inline_fragment.node.selection_set.node,
837                            keys,
838                        ) {
839                            return false;
840                        }
841                    }
842                }
843            }
844            true
845        }
846
847        if let Some(children) = keys.get(field.name.node.as_str()) {
848            selection_set_in_keys(self, &field.selection_set.node, children)
849        } else {
850            false
851        }
852    }
853}
854
855#[inline]
856fn is_list(ty: &Type) -> bool {
857    matches!(ty.base, BaseType::List(_))
858}
859
860fn get_operation<'a>(
861    document: &'a ExecutableDocument,
862    operation_name: Option<&str>,
863) -> &'a Positioned<OperationDefinition> {
864    let operation = if let Some(operation_name) = operation_name {
865        match &document.operations {
866            DocumentOperations::Single(_) => None,
867            DocumentOperations::Multiple(operations) => operations.get(operation_name),
868        }
869    } else {
870        match &document.operations {
871            DocumentOperations::Single(operation) => Some(operation),
872            DocumentOperations::Multiple(map) if map.len() == 1 => {
873                Some(map.iter().next().unwrap().1)
874            }
875            DocumentOperations::Multiple(_) => None,
876        }
877    };
878    operation.expect("The query validator should find this error.")
879}
880
881fn referenced_variables<'a>(
882    selection_set: &SelectionRefSet<'a>,
883    variables: &'a Variables,
884    variable_definitions: &'a [Positioned<VariableDefinition>],
885) -> (VariablesRef<'a>, VariableDefinitionsRef<'a>) {
886    fn referenced_variables_rec<'a>(
887        selection_set: &SelectionRefSet<'a>,
888        variables: &'a Variables,
889        variable_definitions: &'a [Positioned<VariableDefinition>],
890        variables_ref: &mut VariablesRef<'a>,
891        variables_definition_ref: &mut IndexMap<&'a str, &'a VariableDefinition>,
892    ) {
893        for selection in &selection_set.0 {
894            match selection {
895                SelectionRef::FieldRef(field) => {
896                    for (_, value) in &field.field.arguments {
897                        for name in value.node.referenced_variables() {
898                            if let Some((value, definition)) = variables.get(name).zip(
899                                variable_definitions
900                                    .iter()
901                                    .find(|d| d.node.name.node.as_str() == name),
902                            ) {
903                                variables_ref.variables.insert(name, value);
904                                variables_definition_ref.insert(name, &definition.node);
905                            }
906                        }
907                    }
908                }
909                SelectionRef::InlineFragment { selection_set, .. } => {
910                    referenced_variables_rec(
911                        selection_set,
912                        variables,
913                        variable_definitions,
914                        variables_ref,
915                        variables_definition_ref,
916                    );
917                }
918                _ => {}
919            }
920        }
921    }
922
923    let mut variables_ref = VariablesRef::default();
924    let mut variable_definition_ref = IndexMap::new();
925    referenced_variables_rec(
926        selection_set,
927        variables,
928        variable_definitions,
929        &mut variables_ref,
930        &mut variable_definition_ref,
931    );
932    (
933        variables_ref,
934        VariableDefinitionsRef {
935            variables: variable_definition_ref
936                .into_iter()
937                .map(|(_, value)| value)
938                .collect(),
939        },
940    )
941}
942
943#[inline]
944fn is_introspection_field(name: &str) -> bool {
945    name == "__type" || name == "__schema"
946}