2
2
//! final result
3
3
4
4
use anyhow:: { anyhow, Error } ;
5
+ use graph:: constraint_violation;
5
6
use graph:: prelude:: { r, CacheWeight } ;
6
7
use graph:: slog:: warn;
7
8
use graph:: util:: cache_weight:: btree_node_size;
@@ -18,12 +19,12 @@ use graph::{
18
19
prelude:: {
19
20
q, s, ApiSchema , AttributeNames , BlockNumber , ChildMultiplicity , EntityCollection ,
20
21
EntityFilter , EntityLink , EntityOrder , EntityWindow , Logger , ParentLink ,
21
- QueryExecutionError , QueryStore , Value as StoreValue , WindowAttribute ,
22
+ QueryExecutionError , QueryStore , StoreError , Value as StoreValue , WindowAttribute ,
22
23
} ,
23
24
} ;
24
25
25
26
use crate :: execution:: { ExecutionContext , Resolver } ;
26
- use crate :: query:: ast as qast;
27
+ use crate :: query:: ast:: { self as qast, get_argument_value } ;
27
28
use crate :: runner:: ResultSizeMetrics ;
28
29
use crate :: schema:: ast as sast;
29
30
use crate :: store:: { build_query, StoreResolver } ;
@@ -49,6 +50,10 @@ lazy_static! {
49
50
50
51
type GroupedFieldSet < ' a > = IndexMap < & ' a str , CollectedResponseKey < ' a > > ;
51
52
53
+ /// Used for associating objects or interfaces and the field names used in `orderBy` query field
54
+ /// attributes.
55
+ type ComplementaryFields < ' a > = BTreeMap < ObjectOrInterface < ' a > , String > ;
56
+
52
57
/// An `ObjectType` with `Hash` and `Eq` derived from the name.
53
58
#[ derive( Clone , Debug ) ]
54
59
pub struct ObjectCondition < ' a > ( & ' a s:: ObjectType ) ;
@@ -555,10 +560,17 @@ fn execute_root_selection_set(
555
560
) -> Result < Vec < Node > , Vec < QueryExecutionError > > {
556
561
// Obtain the root Query type and fail if there isn't one
557
562
let query_type = ctx. query . schema . query_type . as_ref ( ) . into ( ) ;
558
- let grouped_field_set = collect_fields ( ctx, query_type, once ( selection_set) ) ;
563
+ let ( grouped_field_set, _complementary_fields) =
564
+ collect_fields ( ctx, query_type, once ( selection_set) ) ;
559
565
560
566
// Execute the root selection set against the root query type
561
- execute_selection_set ( resolver, ctx, make_root_node ( ) , grouped_field_set)
567
+ execute_selection_set (
568
+ resolver,
569
+ ctx,
570
+ make_root_node ( ) ,
571
+ grouped_field_set,
572
+ ComplementaryFields :: new ( ) ,
573
+ )
562
574
}
563
575
564
576
fn check_result_size ( logger : & Logger , size : usize ) -> Result < ( ) , QueryExecutionError > {
@@ -576,6 +588,7 @@ fn execute_selection_set<'a>(
576
588
ctx : & ' a ExecutionContext < impl Resolver > ,
577
589
mut parents : Vec < Node > ,
578
590
grouped_field_set : GroupedFieldSet < ' a > ,
591
+ mut complementary_fields : ComplementaryFields < ' a > ,
579
592
) -> Result < Vec < Node > , Vec < QueryExecutionError > > {
580
593
let schema = & ctx. query . schema ;
581
594
let mut errors: Vec < QueryExecutionError > = Vec :: new ( ) ;
@@ -619,7 +632,7 @@ fn execute_selection_set<'a>(
619
632
& field. name ,
620
633
) ;
621
634
// Group fields with the same response key, so we can execute them together
622
- let mut grouped_field_set =
635
+ let ( mut grouped_field_set, new_complementary_fields ) =
623
636
collect_fields ( ctx, child_type, fields. iter ( ) . map ( |f| & f. selection_set ) ) ;
624
637
625
638
// "Select by Specific Attribute Names" is an experimental feature and can be disabled completely.
@@ -630,8 +643,10 @@ fn execute_selection_set<'a>(
630
643
if * DISABLE_EXPERIMENTAL_FEATURE_SELECT_BY_SPECIFIC_ATTRIBUTE_NAMES {
631
644
BTreeMap :: new ( )
632
645
} else {
633
- CollectedAttributeNames :: consolidate_column_names ( & mut grouped_field_set)
634
- . resolve_interfaces ( & ctx. query . schema . types_for_interface ( ) )
646
+ let mut collected =
647
+ CollectedAttributeNames :: consolidate_column_names ( & mut grouped_field_set) ;
648
+ collected. populate_complementary_fields ( & mut complementary_fields) ;
649
+ collected. resolve_interfaces ( & ctx. query . schema . types_for_interface ( ) )
635
650
} ;
636
651
637
652
match execute_field (
@@ -645,7 +660,13 @@ fn execute_selection_set<'a>(
645
660
collected_columns,
646
661
) {
647
662
Ok ( children) => {
648
- match execute_selection_set ( resolver, ctx, children, grouped_field_set) {
663
+ match execute_selection_set (
664
+ resolver,
665
+ ctx,
666
+ children,
667
+ grouped_field_set,
668
+ new_complementary_fields,
669
+ ) {
649
670
Ok ( children) => {
650
671
Join :: perform ( & mut parents, children, response_key) ;
651
672
let weight =
@@ -662,6 +683,23 @@ fn execute_selection_set<'a>(
662
683
}
663
684
}
664
685
686
+ // Confidence check: all complementary fields must be consumed, otherwise constructed SQL
687
+ // queries will be malformed.
688
+ if !* DISABLE_EXPERIMENTAL_FEATURE_SELECT_BY_SPECIFIC_ATTRIBUTE_NAMES {
689
+ complementary_fields
690
+ . into_iter ( )
691
+ . for_each ( |( parent, complementary_field) | {
692
+ errors. push (
693
+ constraint_violation ! (
694
+ "Complementary field \" {}\" was not prefetched by its parent: {}" ,
695
+ complementary_field,
696
+ parent. name( ) . to_string( ) ,
697
+ )
698
+ . into ( ) ,
699
+ )
700
+ } ) ;
701
+ }
702
+
665
703
if errors. is_empty ( ) {
666
704
Ok ( parents)
667
705
} else {
@@ -755,8 +793,9 @@ fn collect_fields<'a>(
755
793
ctx : & ' a ExecutionContext < impl Resolver > ,
756
794
parent_ty : ObjectOrInterface < ' a > ,
757
795
selection_sets : impl Iterator < Item = & ' a q:: SelectionSet > ,
758
- ) -> GroupedFieldSet < ' a > {
796
+ ) -> ( GroupedFieldSet < ' a > , ComplementaryFields < ' a > ) {
759
797
let mut grouped_fields = IndexMap :: new ( ) ;
798
+ let mut complementary_fields = ComplementaryFields :: new ( ) ;
760
799
761
800
for selection_set in selection_sets {
762
801
collect_fields_inner (
@@ -765,6 +804,7 @@ fn collect_fields<'a>(
765
804
selection_set,
766
805
& mut HashSet :: new ( ) ,
767
806
& mut grouped_fields,
807
+ & mut complementary_fields,
768
808
) ;
769
809
}
770
810
@@ -778,7 +818,7 @@ fn collect_fields<'a>(
778
818
}
779
819
}
780
820
781
- grouped_fields
821
+ ( grouped_fields, complementary_fields )
782
822
}
783
823
784
824
// When querying an object type, `type_condition` will always be that object type, even if it passes
@@ -792,6 +832,7 @@ fn collect_fields_inner<'a>(
792
832
selection_set : & ' a q:: SelectionSet ,
793
833
visited_fragments : & mut HashSet < & ' a str > ,
794
834
output : & mut GroupedFieldSet < ' a > ,
835
+ complementary_fields : & mut ComplementaryFields < ' a > ,
795
836
) {
796
837
fn collect_fragment < ' a > (
797
838
ctx : & ' a ExecutionContext < impl Resolver > ,
@@ -800,6 +841,7 @@ fn collect_fields_inner<'a>(
800
841
frag_selection_set : & ' a q:: SelectionSet ,
801
842
visited_fragments : & mut HashSet < & ' a str > ,
802
843
output : & mut GroupedFieldSet < ' a > ,
844
+ complementary_fields : & mut ComplementaryFields < ' a > ,
803
845
) {
804
846
let schema = & ctx. query . schema . document ( ) ;
805
847
let fragment_ty = match frag_ty_condition {
@@ -820,6 +862,7 @@ fn collect_fields_inner<'a>(
820
862
& frag_selection_set,
821
863
visited_fragments,
822
864
output,
865
+ complementary_fields,
823
866
) ;
824
867
} else {
825
868
// This is an interface fragment in the root selection for an interface.
@@ -842,6 +885,7 @@ fn collect_fields_inner<'a>(
842
885
& frag_selection_set,
843
886
visited_fragments,
844
887
output,
888
+ complementary_fields,
845
889
) ;
846
890
}
847
891
}
@@ -863,6 +907,34 @@ fn collect_fields_inner<'a>(
863
907
type_condition,
864
908
field,
865
909
) ;
910
+
911
+ // Collect complementary fields used in the `orderBy` query attribute, if present.
912
+ if let Some ( arguments) = get_argument_value ( & field. arguments , "orderBy" ) {
913
+ let schema_field = type_condition. field ( & field. name ) . expect ( & format ! (
914
+ "the field {:?} to exist in {:?}" ,
915
+ & field. name,
916
+ & type_condition. name( )
917
+ ) ) ;
918
+ let field_name = sast:: get_field_name ( & schema_field. field_type ) ;
919
+ let object_or_interface_for_field = ctx
920
+ . query
921
+ . schema
922
+ . document ( )
923
+ . object_or_interface ( & field_name)
924
+ . expect ( & format ! (
925
+ "The field {:?} to exist in the Document" ,
926
+ field_name
927
+ ) ) ;
928
+ match arguments {
929
+ graphql_parser:: schema:: Value :: Enum ( complementary_field_name) => {
930
+ complementary_fields. insert (
931
+ object_or_interface_for_field,
932
+ complementary_field_name. clone ( ) ,
933
+ ) ;
934
+ }
935
+ _ => unimplemented ! ( "unsure on what to do about other variants" ) ,
936
+ }
937
+ }
866
938
}
867
939
868
940
q:: Selection :: FragmentSpread ( spread) => {
@@ -878,6 +950,7 @@ fn collect_fields_inner<'a>(
878
950
& fragment. selection_set ,
879
951
visited_fragments,
880
952
output,
953
+ complementary_fields,
881
954
) ;
882
955
}
883
956
}
@@ -890,6 +963,7 @@ fn collect_fields_inner<'a>(
890
963
& fragment. selection_set ,
891
964
visited_fragments,
892
965
output,
966
+ complementary_fields,
893
967
) ;
894
968
}
895
969
}
@@ -999,7 +1073,22 @@ impl<'a> CollectedAttributeNames<'a> {
999
1073
self . 0
1000
1074
. entry ( object_or_interface)
1001
1075
. or_insert ( AttributeNames :: All )
1002
- . update ( field) ;
1076
+ . add ( field) ;
1077
+ }
1078
+
1079
+ /// Injects complementary fields that were collected priviously in upper hierarchical levels of
1080
+ /// the query into `self`.
1081
+ fn populate_complementary_fields (
1082
+ & mut self ,
1083
+ complementary_fields : & mut ComplementaryFields < ' a > ,
1084
+ ) {
1085
+ for ( object_or_interface, selected_attributes) in self . 0 . iter_mut ( ) {
1086
+ if let Some ( complementary_field_name) =
1087
+ complementary_fields. remove ( & object_or_interface)
1088
+ {
1089
+ selected_attributes. add_str ( & complementary_field_name)
1090
+ }
1091
+ }
1003
1092
}
1004
1093
1005
1094
/// Consume this instance and transform it into a mapping from
@@ -1090,7 +1179,7 @@ fn filter_derived_fields(
1090
1179
None // field does not exist
1091
1180
}
1092
1181
} )
1093
- . for_each ( |col| filtered. insert ( & col) ) ;
1182
+ . for_each ( |col| filtered. add_str ( & col) ) ;
1094
1183
filtered
1095
1184
}
1096
1185
}
0 commit comments