apollo_federation/query_plan/mod.rs
1use std::sync::Arc;
2
3use apollo_compiler::ExecutableDocument;
4use apollo_compiler::Name;
5use apollo_compiler::executable;
6use serde::Deserialize;
7use serde::Serialize;
8
9use crate::query_plan::query_planner::QueryPlanningStatistics;
10
11pub(crate) mod conditions;
12pub(crate) mod display;
13pub(crate) mod fetch_dependency_graph;
14pub(crate) mod fetch_dependency_graph_processor;
15pub mod generate;
16pub mod query_planner;
17pub(crate) mod query_planning_traversal;
18pub mod requires_selection;
19pub mod serializable_document;
20
21pub type QueryPlanCost = f64;
22
23#[derive(Debug, Default, PartialEq, Serialize, Deserialize)]
24pub struct QueryPlan {
25 pub node: Option<TopLevelPlanNode>,
26 pub statistics: QueryPlanningStatistics,
27}
28
29#[derive(Debug, PartialEq, derive_more::From, Serialize, Deserialize)]
30pub enum TopLevelPlanNode {
31 Subscription(SubscriptionNode),
32 #[from(FetchNode, Box<FetchNode>)]
33 Fetch(Box<FetchNode>),
34 Sequence(SequenceNode),
35 Parallel(ParallelNode),
36 Flatten(FlattenNode),
37 Defer(DeferNode),
38 #[from(ConditionNode, Box<ConditionNode>)]
39 Condition(Box<ConditionNode>),
40}
41
42#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
43pub struct SubscriptionNode {
44 pub primary: Box<FetchNode>,
45 // XXX(@goto-bus-stop) Is this not just always a SequenceNode?
46 pub rest: Option<Box<PlanNode>>,
47}
48
49#[derive(Debug, Clone, PartialEq, derive_more::From, Serialize, Deserialize)]
50pub enum PlanNode {
51 #[from(FetchNode, Box<FetchNode>)]
52 Fetch(Box<FetchNode>),
53 Sequence(SequenceNode),
54 Parallel(ParallelNode),
55 Flatten(FlattenNode),
56 Defer(DeferNode),
57 #[from(ConditionNode, Box<ConditionNode>)]
58 Condition(Box<ConditionNode>),
59}
60
61#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
62pub struct FetchNode {
63 pub subgraph_name: Arc<str>,
64 /// Optional identifier for the fetch for defer support. All fetches of a given plan will be
65 /// guaranteed to have a unique `id`.
66 pub id: Option<u64>,
67 pub variable_usages: Vec<Name>,
68 #[serde(skip_serializing_if = "Vec::is_empty")]
69 #[serde(default)]
70 pub requires: Vec<requires_selection::Selection>,
71 // PORT_NOTE: We don't serialize the "operation" string in this struct, as these query plan
72 // nodes are meant for direct consumption by router (without any serdes), so we leave the
73 // question of whether it needs to be serialized to router.
74 pub operation_document: serializable_document::SerializableDocument,
75 pub operation_name: Option<Name>,
76 #[serde(with = "crate::utils::serde_bridge::operation_type")]
77 pub operation_kind: executable::OperationType,
78 /// Optionally describe a number of "rewrites" that query plan executors should apply to the
79 /// data that is sent as the input of this fetch. Note that such rewrites should only impact the
80 /// inputs of the fetch they are applied to (meaning that, as those inputs are collected from
81 /// the current in-memory result, the rewrite should _not_ impact said in-memory results, only
82 /// what is sent in the fetch).
83 pub input_rewrites: Arc<Vec<Arc<FetchDataRewrite>>>,
84 /// Similar to `input_rewrites`, but for optional "rewrites" to apply to the data that is
85 /// received from a fetch (and before it is applied to the current in-memory results).
86 pub output_rewrites: Vec<Arc<FetchDataRewrite>>,
87 /// Similar to the other kinds of rewrites. This is a mechanism to convert a contextual path into
88 /// an argument to a resolver. Note value setters are currently unused here, but may be used in
89 /// the future.
90 pub context_rewrites: Vec<Arc<FetchDataRewrite>>,
91}
92
93#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
94pub struct SequenceNode {
95 pub nodes: Vec<PlanNode>,
96}
97
98#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
99pub struct ParallelNode {
100 pub nodes: Vec<PlanNode>,
101}
102
103#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
104pub struct FlattenNode {
105 pub path: Vec<FetchDataPathElement>,
106 pub node: Box<PlanNode>,
107}
108
109/// A `DeferNode` corresponds to one or more `@defer` applications at the same level of "nestedness"
110/// in the planned query.
111///
112/// It contains a "primary block" and a vector of "deferred blocks". The "primary block" represents
113/// the part of the query that is _not_ deferred (so the part of the query up until we reach the
114/// @defer(s) this handles), while each "deferred block" correspond to the deferred part of one of
115/// the @defer(s) handled by the node.
116///
117/// Note that `DeferNode`s are only generated if defer support is enabled for the query planner.
118/// Also note that if said support is enabled, then `DeferNode`s are always generated if the query
119/// has a @defer application, even if in some cases generated plan may not "truly" defer the
120/// underlying fetches (i.e. in cases where `deferred[*].node` are all undefined). This currently
121/// happens because some specific cases of defer cannot be handled, but could later also happen if
122/// we implement more advanced server-side heuristics to decide if deferring is judicious or not.
123/// This allows the executor of the plan to consistently send a defer-abiding multipart response to
124/// the client.
125#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
126pub struct DeferNode {
127 /// The "primary" part of a defer, that is the non-deferred part (though could be deferred
128 /// itself for a nested defer).
129 pub primary: PrimaryDeferBlock,
130 /// The "deferred" parts of the defer (note that it's a vector). Each of those deferred elements
131 /// will correspond to a different chunk of the response to the client (after the initial
132 /// on-deferred one that is).
133 pub deferred: Vec<DeferredDeferBlock>,
134}
135
136/// The primary block of a `DeferNode`.
137#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
138pub struct PrimaryDeferBlock {
139 /// The part of the original query that "selects" the data to send in that primary response
140 /// once the plan in `node` completes). Note that if the parent `DeferNode` is nested, then it
141 /// must come inside the `DeferredNode` in which it is nested, and in that case this
142 /// sub-selection will start at that parent `DeferredNode.query_path`. Note that this can be
143 /// `None` in the rare case that everything in the original query is deferred (which is not very
144 /// useful in practice, but not disallowed by the @defer spec at the moment).
145 pub sub_selection: Option<String>,
146 /// The plan to get all the data for the primary block. Same notes as for subselection: usually
147 /// defined, but can be undefined in some corner cases where nothing is to be done in the
148 /// primary block.
149 pub node: Option<Box<PlanNode>>,
150}
151
152/// A deferred block of a `DeferNode`.
153#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
154pub struct DeferredDeferBlock {
155 /// References one or more fetch node(s) (by `id`) within `DeferNode.primary.node`. The plan of
156 /// this deferred part should not be started until all such fetches return.
157 pub depends: Vec<DeferredDependency>,
158 /// The optional defer label.
159 pub label: Option<String>,
160 /// Path, in the query, to the `@defer` application this corresponds to. The `sub_selection`
161 /// starts at this `query_path`.
162 pub query_path: Vec<QueryPathElement>,
163 /// The part of the original query that "selects" the data to send in the deferred response
164 /// (once the plan in `node` completes). Will be set _unless_ `node` is a `DeferNode` itself.
165 pub sub_selection: Option<String>,
166 /// The plan to get all the data for this deferred block. Usually set, but can be `None` for a
167 /// `@defer` application where everything has been fetched in the "primary block" (i.e. when
168 /// this deferred block only exists to expose what should be send to the upstream client in a
169 /// deferred response), but without declaring additional fetches. This happens for @defer
170 /// applications that cannot be handled through the query planner and where the defer cannot be
171 /// passed through to the subgraph).
172 pub node: Option<Box<PlanNode>>,
173}
174
175#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
176pub struct DeferredDependency {
177 /// A `FetchNode` ID.
178 pub id: String,
179}
180
181#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
182pub struct ConditionNode {
183 pub condition_variable: Name,
184 pub if_clause: Option<Box<PlanNode>>,
185 pub else_clause: Option<Box<PlanNode>>,
186}
187
188/// The type of rewrites currently supported on the input/output data of fetches.
189///
190/// A rewrite usually identifies some sub-part of the data and some action to perform on that
191/// sub-part.
192#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, derive_more::From)]
193pub enum FetchDataRewrite {
194 ValueSetter(FetchDataValueSetter),
195 KeyRenamer(FetchDataKeyRenamer),
196}
197
198/// A rewrite that sets a value at the provided path of the data it is applied to.
199#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
200pub struct FetchDataValueSetter {
201 /// Path to the value that is set by this "rewrite".
202 pub path: Vec<FetchDataPathElement>,
203 /// The value to set at `path`. Note that the query planner currently only uses string values,
204 /// but that may change in the future.
205 pub set_value_to: serde_json_bytes::Value,
206}
207
208/// A rewrite that renames the key at the provided path of the data it is applied to.
209#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
210pub struct FetchDataKeyRenamer {
211 /// Path to the key that is renamed by this "rewrite".
212 pub path: Vec<FetchDataPathElement>,
213 /// The key to rename to at `path`.
214 pub rename_key_to: Name,
215}
216
217/// Vectors of this element match path(s) to a value in fetch data. Each element is (1) a key in
218/// object data, (2) _any_ index in array data (often serialized as `@`), (3) a typename constraint
219/// on the object data at that point in the path(s) (a path should only match for objects whose
220/// `__typename` is the provided type), or (4) a parent indicator to move upwards one level in the
221/// object.
222///
223/// It's possible for vectors of this element to match no paths in fetch data, e.g. if an object key
224/// doesn't exist, or if an object's `__typename` doesn't equal the provided one. If this occurs,
225/// then query plan execution should not execute the instruction this path is associated with.
226///
227/// The path starts at the top of the data it is applied to. So for instance, for fetch data inputs,
228/// the path starts at the root of the object representing those inputs.
229///
230/// Note that the `@` is currently optional in some contexts, as query plan execution may assume
231/// upon encountering array data in a path that it should match the remaining path to the array's
232/// elements.
233#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
234pub enum FetchDataPathElement {
235 Key(Name, Option<Conditions>),
236 AnyIndex(Option<Conditions>),
237 TypenameEquals(Name),
238 Parent,
239}
240
241pub type Conditions = Vec<Name>;
242
243/// Vectors of this element match a path in a query. Each element is (1) a field in a query, or (2)
244/// an inline fragment in a query.
245#[derive(Debug, Clone, PartialEq, serde::Serialize, Deserialize)]
246pub enum QueryPathElement {
247 Field { response_key: Name },
248 InlineFragment { type_condition: Name },
249}
250
251impl PlanNode {
252 /// Returns the kind of plan node this is as a human-readable string. Exact output not guaranteed.
253 fn node_kind(&self) -> &'static str {
254 match self {
255 Self::Fetch(_) => "Fetch",
256 Self::Sequence(_) => "Sequence",
257 Self::Parallel(_) => "Parallel",
258 Self::Flatten(_) => "Flatten",
259 Self::Defer(_) => "Defer",
260 Self::Condition(_) => "Condition",
261 }
262 }
263}