Skip to content

Commit 335dbb0

Browse files
committed
perf(query-compiler): own required nested set phase
Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
1 parent 745b860 commit 335dbb0

6 files changed

Lines changed: 288 additions & 33 deletions

File tree

query-compiler/core/src/query_graph/formatters.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ impl Display for Computation {
6666
match self {
6767
Self::DiffLeftToRight(_) => write!(f, "DiffLeftToRight"),
6868
Self::DiffRightToLeft(_) => write!(f, "DiffRightToLeft"),
69+
Self::RequiredOneToManySet(_) => write!(f, "RequiredOneToManySet"),
6970
}
7071
}
7172
}

query-compiler/core/src/query_graph/mod.rs

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ use petgraph::{
2121
visit::{EdgeRef as PEdgeRef, NodeIndexable},
2222
*,
2323
};
24-
use query_structure::{FieldSelection, Filter, Placeholder, QueryArguments, SelectionResult, WriteArgs};
24+
use query_structure::{
25+
FieldSelection, Filter, Model, Placeholder, PrismaValue, QueryArguments, SelectionResult, WriteArgs,
26+
};
2527

2628
pub type QueryGraphResult<T> = std::result::Result<T, QueryGraphError>;
2729

@@ -93,6 +95,7 @@ impl Flow {
9395
pub enum Computation {
9496
DiffLeftToRight(DiffNode),
9597
DiffRightToLeft(DiffNode),
98+
RequiredOneToManySet(RequiredOneToManySetNode),
9699
}
97100

98101
impl Computation {
@@ -103,6 +106,28 @@ impl Computation {
103106
pub fn empty_diff_right_to_left(fields: FieldSelection) -> Self {
104107
Self::DiffRightToLeft(DiffNode::new_empty(fields))
105108
}
109+
110+
pub fn required_one_to_many_set(
111+
fields: FieldSelection,
112+
parent_node: NodeRef,
113+
parent_link: FieldSelection,
114+
child_link: FieldSelection,
115+
child_model: Model,
116+
parent_expectation: DataExpectation,
117+
relation_expectation: DataExpectation,
118+
request_now: PrismaValue,
119+
) -> Self {
120+
Self::RequiredOneToManySet(RequiredOneToManySetNode::new(
121+
fields,
122+
parent_node,
123+
parent_link,
124+
child_link,
125+
child_model,
126+
parent_expectation,
127+
relation_expectation,
128+
request_now,
129+
))
130+
}
106131
}
107132

108133
pub struct DiffNode {
@@ -121,6 +146,45 @@ impl DiffNode {
121146
}
122147
}
123148

149+
pub struct RequiredOneToManySetNode {
150+
pub old_children: Option<Placeholder>,
151+
pub new_children: Option<Placeholder>,
152+
pub fields: FieldSelection,
153+
pub parent_node: NodeRef,
154+
pub parent_link: FieldSelection,
155+
pub child_link: FieldSelection,
156+
pub child_model: Model,
157+
pub parent_expectation: DataExpectation,
158+
pub relation_expectation: DataExpectation,
159+
pub request_now: PrismaValue,
160+
}
161+
162+
impl RequiredOneToManySetNode {
163+
pub fn new(
164+
fields: FieldSelection,
165+
parent_node: NodeRef,
166+
parent_link: FieldSelection,
167+
child_link: FieldSelection,
168+
child_model: Model,
169+
parent_expectation: DataExpectation,
170+
relation_expectation: DataExpectation,
171+
request_now: PrismaValue,
172+
) -> Self {
173+
Self {
174+
old_children: None,
175+
new_children: None,
176+
fields,
177+
parent_node,
178+
parent_link,
179+
child_link,
180+
child_model,
181+
parent_expectation,
182+
relation_expectation,
183+
request_now,
184+
}
185+
}
186+
}
187+
124188
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
125189
pub struct NodeRef {
126190
node_ix: NodeIndex,

query-compiler/core/src/query_graph_builder/inputs.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,18 @@ node_input_field!(
9494
Node::Computation(Computation::DiffLeftToRight(diff_node) | Computation::DiffRightToLeft(diff_node)) => &mut diff_node.right
9595
);
9696

97+
node_input_field!(
98+
RequiredOneToManySetOldInput,
99+
Option<Placeholder>,
100+
Node::Computation(Computation::RequiredOneToManySet(set_node)) => &mut set_node.old_children
101+
);
102+
103+
node_input_field!(
104+
RequiredOneToManySetNewInput,
105+
Option<Placeholder>,
106+
Node::Computation(Computation::RequiredOneToManySet(set_node)) => &mut set_node.new_children
107+
);
108+
97109
node_input_field!(
98110
IfInput,
99111
Option<Placeholder>,

query-compiler/core/src/query_graph_builder/write/nested/set_nested.rs

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ use super::*;
22
use crate::{
33
ParsedInputValue,
44
inputs::{
5-
IfInput, LeftSideDiffInput, RightSideDiffInput, UpdateManyRecordsSelectorsInput, UpdateOrCreateArgsInput,
5+
IfInput, LeftSideDiffInput, RequiredOneToManySetNewInput, RequiredOneToManySetOldInput, RightSideDiffInput,
6+
UpdateManyRecordsSelectorsInput, UpdateOrCreateArgsInput,
67
},
78
query_graph::*,
89
};
@@ -165,13 +166,61 @@ fn handle_one_to_many(
165166
let child_link = parent_relation_field.related_field().linking_fields();
166167
let parent_link = parent_relation_field.linking_fields();
167168
let empty_child_link = SelectionResult::from(&child_link);
169+
let child_side_required = parent_relation_field.related_field().is_required();
168170

169171
let child_model = parent_relation_field.related_model();
170172
let read_old_node =
171173
utils::insert_find_children_by_parent_node(graph, parent_node, parent_relation_field, Filter::empty())?;
172174

173175
let read_new_query = utils::read_ids_infallible(child_model.clone(), child_model_identifier.clone(), filter);
174176
let read_new_node = graph.create_node(read_new_query);
177+
178+
if child_side_required
179+
&& child_model_identifier.selections().len() == 1
180+
&& parent_link.selections().len() == 1
181+
&& child_link.selections().len() == 1
182+
{
183+
let set_node = graph.create_node(Node::Computation(Computation::required_one_to_many_set(
184+
child_model_identifier.clone(),
185+
*parent_node,
186+
parent_link,
187+
child_link,
188+
child_model,
189+
DataExpectation::non_empty_rows(
190+
MissingRelatedRecord::builder()
191+
.model(&parent_relation_field.model())
192+
.relation(&parent_relation_field.relation())
193+
.operation(DataOperation::NestedSet)
194+
.build(),
195+
),
196+
DataExpectation::empty_rows(RelationViolation::from(parent_relation_field.clone())),
197+
crate::request_context::get_request_now(),
198+
)));
199+
200+
graph.create_edge(&read_old_node, &read_new_node, QueryGraphDependency::ExecutionOrder)?;
201+
202+
graph.create_edge(
203+
&read_old_node,
204+
&set_node,
205+
QueryGraphDependency::ProjectedDataDependency(
206+
child_model_identifier.clone(),
207+
RowSink::ProjectedPlaceholder(&RequiredOneToManySetOldInput),
208+
None,
209+
),
210+
)?;
211+
graph.create_edge(
212+
&read_new_node,
213+
&set_node,
214+
QueryGraphDependency::ProjectedDataDependency(
215+
child_model_identifier,
216+
RowSink::ProjectedPlaceholder(&RequiredOneToManySetNewInput),
217+
None,
218+
),
219+
)?;
220+
221+
return Ok(());
222+
}
223+
175224
let diff_left_to_right_node = graph.create_node(Node::Computation(Computation::empty_diff_left_to_right(
176225
child_model_identifier.clone(),
177226
)));
@@ -269,7 +318,6 @@ fn handle_one_to_many(
269318
let update_disconnect_node =
270319
utils::update_records_node_placeholder_with_args(graph, Filter::empty(), child_model, write_args);
271320

272-
let child_side_required = parent_relation_field.related_field().is_required();
273321
let rf = parent_relation_field.clone();
274322

275323
graph.create_edge(

query-compiler/query-compiler/src/translate.rs

Lines changed: 152 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ use query::translate_query;
1111
use query_builder::QueryBuilder;
1212
use query_core::{
1313
Computation, EdgeRef, Flow, Node, NodeRef, Query, QueryGraph, QueryGraphBuilderError, QueryGraphDependency,
14-
QueryGraphError, RowCountSink, RowSink,
14+
QueryGraphError, RowCountSink, RowSink, UpdateManyRecords, WriteQuery,
1515
};
1616
use query_structure::{
17-
FieldSelection, FieldTypeInformation, IntoFilter, Placeholder, PrismaValue, PrismaValueType, SelectedField,
18-
SelectionResult,
17+
FieldSelection, FieldTypeInformation, IntoFilter, Placeholder, PrismaValue, PrismaValueType, RecordFilter,
18+
SelectedField, SelectionResult, WriteArgs,
1919
};
2020
use thiserror::Error;
2121

@@ -181,6 +181,7 @@ impl<'a, 'b> NodeTranslator<'a, 'b> {
181181
Node::Flow(Flow::Return(_)) => self.translate_return(),
182182
Node::Computation(Computation::DiffLeftToRight(_)) => self.translate_diff_left_to_right(),
183183
Node::Computation(Computation::DiffRightToLeft(_)) => self.translate_diff_right_to_left(),
184+
Node::Computation(Computation::RequiredOneToManySet(_)) => self.translate_required_one_to_many_set(),
184185
}
185186
}
186187

@@ -348,6 +349,139 @@ impl<'a, 'b> NodeTranslator<'a, 'b> {
348349
Ok(self.wrap_children_with_expr(expr, children))
349350
}
350351

352+
fn translate_required_one_to_many_set(&mut self) -> TranslateResult<Expression> {
353+
let children = self.translate_children()?;
354+
355+
let node = self.graph.pluck_node(&self.node);
356+
let node = self.transform_node(node)?;
357+
358+
let Node::Computation(Computation::RequiredOneToManySet(set)) = node else {
359+
panic!("current node must be Computation::RequiredOneToManySet");
360+
};
361+
362+
let old_children = projected_placeholder(set.old_children, "RequiredOneToManySet.old_children")?;
363+
let new_children = projected_placeholder(set.new_children, "RequiredOneToManySet.new_children")?;
364+
let left_diff_name = binding::node_result(self.node);
365+
366+
let selector = SelectionResult::new(
367+
set.fields
368+
.selections()
369+
.map(|field| {
370+
(
371+
field.clone(),
372+
PrismaValue::Placeholder(Placeholder::new(
373+
binding::projected_dependency(self.node, field),
374+
selected_field_placeholder_type(field, true),
375+
)),
376+
)
377+
})
378+
.collect(),
379+
);
380+
381+
let mut update_args = WriteArgs::from_result(
382+
SelectionResult::new(
383+
set.parent_link
384+
.selections()
385+
.zip(set.child_link.selections())
386+
.map(|(parent_field, child_field)| {
387+
(
388+
child_field.clone(),
389+
PrismaValue::Placeholder(Placeholder::new(
390+
binding::projected_dependency(set.parent_node, parent_field),
391+
selected_field_placeholder_type(parent_field, false),
392+
)),
393+
)
394+
})
395+
.collect(),
396+
),
397+
set.request_now,
398+
);
399+
update_args.update_datetimes(&set.child_model);
400+
401+
let update = UpdateManyRecords {
402+
name: String::new(),
403+
model: set.child_model,
404+
record_filter: RecordFilter::from(vec![selector]),
405+
args: update_args,
406+
selected_fields: None,
407+
limit: None,
408+
};
409+
410+
let update_expr = translate_query(Query::Write(WriteQuery::UpdateManyRecords(update)), self.query_builder)?;
411+
let parent_validation = Expression::validate_expectation(
412+
&set.parent_expectation,
413+
Expression::Get {
414+
name: binding::node_result(set.parent_node),
415+
},
416+
);
417+
418+
let relation_validation = Expression::validate_expectation(
419+
&set.relation_expectation,
420+
Expression::Diff {
421+
from: Expression::Get {
422+
name: old_children.name.clone(),
423+
}
424+
.into(),
425+
to: Expression::Get {
426+
name: new_children.name.clone(),
427+
}
428+
.into(),
429+
fields: set.fields.db_names().collect(),
430+
},
431+
);
432+
433+
let expr = Expression::Let {
434+
bindings: vec![Binding::new(
435+
left_diff_name.clone(),
436+
Expression::Diff {
437+
from: Expression::Get {
438+
name: new_children.name.clone(),
439+
}
440+
.into(),
441+
to: Expression::Get {
442+
name: old_children.name.clone(),
443+
}
444+
.into(),
445+
fields: set.fields.db_names().collect(),
446+
},
447+
)],
448+
expr: Expression::Seq(vec![
449+
Expression::If {
450+
value: Expression::Get {
451+
name: left_diff_name.clone(),
452+
}
453+
.into(),
454+
rule: query_core::DataRule::RowCountNeq(0),
455+
then: Expression::Let {
456+
bindings: set
457+
.fields
458+
.selections()
459+
.map(|field| {
460+
Binding::new(
461+
binding::projected_dependency(self.node, field),
462+
Expression::MapField {
463+
field: field.db_name().into(),
464+
records: Expression::Get {
465+
name: left_diff_name.clone(),
466+
}
467+
.into(),
468+
},
469+
)
470+
})
471+
.collect(),
472+
expr: Expression::Seq(vec![parent_validation, update_expr]).into(),
473+
}
474+
.into(),
475+
r#else: Expression::Unit.into(),
476+
},
477+
relation_validation,
478+
])
479+
.into(),
480+
};
481+
482+
Ok(self.wrap_children_with_expr(expr, children))
483+
}
484+
351485
fn transform_node(&mut self, mut node: Node) -> TranslateResult<Node> {
352486
for edge in self.parent_edges {
353487
match self.graph.take_edge(edge) {
@@ -628,16 +762,7 @@ impl<'a, 'b> NodeTranslator<'a, 'b> {
628762
))
629763
})?;
630764

631-
let r#type = field
632-
.type_info()
633-
.as_ref()
634-
.map(FieldTypeInformation::to_prisma_type)
635-
.unwrap_or(PrismaValueType::Any);
636-
let r#type = if binding_is_unique {
637-
r#type
638-
} else {
639-
PrismaValueType::List(r#type.into())
640-
};
765+
let r#type = selected_field_placeholder_type(field, !binding_is_unique);
641766

642767
Ok(Placeholder {
643768
name: if bindings_refer_to_fields {
@@ -649,3 +774,17 @@ impl<'a, 'b> NodeTranslator<'a, 'b> {
649774
})
650775
}
651776
}
777+
778+
fn selected_field_placeholder_type(field: &SelectedField, list: bool) -> PrismaValueType {
779+
let r#type = field
780+
.type_info()
781+
.as_ref()
782+
.map(FieldTypeInformation::to_prisma_type)
783+
.unwrap_or(PrismaValueType::Any);
784+
785+
if list {
786+
PrismaValueType::List(r#type.into())
787+
} else {
788+
r#type
789+
}
790+
}

0 commit comments

Comments
 (0)