Skip to main content

teaql_runtime/repository/
graph.rs

1use std::collections::BTreeMap;
2
3use teaql_core::{
4    DeleteCommand, Entity, EntityDescriptor, Expr, InsertCommand, PropertyDescriptor, Record,
5    SelectQuery, UpdateCommand, Value,
6};
7use teaql_sql::SqlDialect;
8
9use crate::{
10    GraphMutationKind, GraphMutationPlan, GraphNode, GraphOperation, RepositoryError, RuntimeError,
11    sorted_update_fields,
12};
13
14use super::{GraphTransactionBoundary, QueryExecutor, ResolvedRepository, helpers::*};
15
16impl<'a, D, E> ResolvedRepository<'a, D, E>
17where
18    D: SqlDialect,
19    E: QueryExecutor,
20{
21    pub fn save_graph(&self, node: GraphNode) -> Result<GraphNode, RepositoryError<E::Error>> {
22        if node.entity != self.entity {
23            return Err(RepositoryError::Runtime(RuntimeError::Graph(format!(
24                "resolved repository {} cannot save graph root {}",
25                self.entity, node.entity
26            ))));
27        }
28        let boundary = self
29            .repository
30            .executor
31            .begin_transaction()
32            .map_err(RepositoryError::Executor)?;
33        if matches!(boundary, GraphTransactionBoundary::Unsupported) {
34            return Err(RepositoryError::Runtime(RuntimeError::Graph(
35                "save_graph requires a transactional executor".to_owned(),
36            )));
37        }
38        let result = self
39            .plan_graph(node)
40            .and_then(|plan| self.execute_graph_plan(plan));
41        match result {
42            Ok(saved) => {
43                if matches!(boundary, GraphTransactionBoundary::Started) {
44                    self.repository
45                        .executor
46                        .commit_transaction()
47                        .map_err(RepositoryError::Executor)?;
48                }
49                Ok(saved)
50            }
51            Err(err) => {
52                if !matches!(boundary, GraphTransactionBoundary::Unsupported) {
53                    self.repository
54                        .executor
55                        .rollback_transaction()
56                        .map_err(RepositoryError::Executor)?;
57                }
58                Err(err)
59            }
60        }
61    }
62
63    pub fn save_entity_graph<T>(&self, entity: T) -> Result<GraphNode, RepositoryError<E::Error>>
64    where
65        T: Entity,
66    {
67        let node = self
68            .graph_node_from_entity(entity)
69            .map_err(RepositoryError::Runtime)?;
70        self.save_graph(node)
71    }
72
73    pub fn plan_graph(
74        &self,
75        node: GraphNode,
76    ) -> Result<GraphMutationPlan, RepositoryError<E::Error>> {
77        if node.entity != self.entity {
78            return Err(RepositoryError::Runtime(RuntimeError::Graph(format!(
79                "resolved repository {} cannot plan graph root {}",
80                self.entity, node.entity
81            ))));
82        }
83        let mut node = node;
84        let mut plan = GraphMutationPlan::default();
85        self.collect_graph_plan(&mut node, &mut plan)?;
86        plan.planned_root = Some(node);
87        plan.rebuild_batches();
88        Ok(plan)
89    }
90
91    pub fn execute_graph_plan(
92        &self,
93        plan: GraphMutationPlan,
94    ) -> Result<GraphNode, RepositoryError<E::Error>> {
95        let Some(root) = plan.planned_root else {
96            return Err(RepositoryError::Runtime(RuntimeError::Graph(
97                "graph mutation plan has no planned root".to_owned(),
98            )));
99        };
100        self.upsert_graph_node(root)
101    }
102
103    pub fn graph_node_from_entity<T>(&self, entity: T) -> Result<GraphNode, RuntimeError>
104    where
105        T: Entity,
106    {
107        let descriptor = T::entity_descriptor();
108        if descriptor.name != self.entity {
109            return Err(RuntimeError::Graph(format!(
110                "resolved repository {} cannot extract graph root {}",
111                self.entity, descriptor.name
112            )));
113        }
114        self.graph_node_from_record(&descriptor.name, entity.into_record())
115    }
116
117    fn collect_graph_plan(
118        &self,
119        node: &mut GraphNode,
120        plan: &mut GraphMutationPlan,
121    ) -> Result<(), RepositoryError<E::Error>> {
122        match node.operation {
123            GraphOperation::Reference => {
124                plan.push(
125                    node.entity.clone(),
126                    GraphMutationKind::Reference,
127                    node.values.clone(),
128                    Vec::new(),
129                );
130                return Ok(());
131            }
132            GraphOperation::Remove => {
133                plan.push(
134                    node.entity.clone(),
135                    GraphMutationKind::Delete,
136                    node.values.clone(),
137                    Vec::new(),
138                );
139                return Ok(());
140            }
141            GraphOperation::Upsert => {}
142        }
143
144        let descriptor = self
145            .repository
146            .metadata
147            .context
148            .require_entity(&node.entity)
149            .map_err(RepositoryError::Runtime)?;
150        let id_property = descriptor.id_property().cloned();
151        let id = id_property.as_ref().and_then(|property| {
152            node.values
153                .get(&property.name)
154                .filter(|value| !is_unassigned_id_value(value))
155                .cloned()
156        });
157        let is_update = match (id_property.as_ref(), id.as_ref()) {
158            (Some(id_property), Some(id)) => self
159                .fetch_graph_current_row(&node.entity, &id_property.name, id)?
160                .is_some(),
161            _ => false,
162        };
163        if !is_update {
164            if let Some(id_property) = id_property.as_ref() {
165                let needs_id = !node.values.contains_key(&id_property.name)
166                    || node
167                        .values
168                        .get(&id_property.name)
169                        .is_some_and(is_unassigned_id_value);
170                if needs_id {
171                    let id = self
172                        .repository
173                        .metadata
174                        .context
175                        .next_id(&node.entity)
176                        .map_err(RepositoryError::Runtime)?;
177                    node.values.insert(id_property.name.clone(), Value::U64(id));
178                }
179            }
180            ensure_initial_version(&mut node.values, descriptor);
181        }
182        let update_fields = if is_update {
183            let mut excluded = Vec::new();
184            if let Some(id_property) = id_property.as_ref() {
185                excluded.push(id_property.name.clone());
186            }
187            if let Some(version_property) = descriptor.version_property() {
188                excluded.push(version_property.name.clone());
189            }
190            sorted_update_fields(&node.values, excluded)
191        } else {
192            Vec::new()
193        };
194        plan.push(
195            node.entity.clone(),
196            if is_update {
197                GraphMutationKind::Update
198            } else {
199                GraphMutationKind::Create
200            },
201            node.values.clone(),
202            update_fields,
203        );
204
205        for (name, children) in &mut node.relations {
206            let relation = descriptor.relation_by_name(name).ok_or_else(|| {
207                RepositoryError::Runtime(RuntimeError::MissingRelation {
208                    entity: node.entity.clone(),
209                    relation: name.clone(),
210                })
211            })?;
212            let child_repo = self.scoped_repository(relation.target_entity.clone());
213            for child in children {
214                ensure_relation_target(&node.entity, name, &relation.target_entity, child)?;
215                child_repo.collect_graph_plan(child, plan)?;
216            }
217        }
218        Ok(())
219    }
220
221    fn insert_graph_node(
222        &self,
223        mut node: GraphNode,
224    ) -> Result<GraphNode, RepositoryError<E::Error>> {
225        match node.operation {
226            GraphOperation::Upsert => {}
227            GraphOperation::Reference => return self.validate_reference_node(node),
228            GraphOperation::Remove => {
229                return Err(RepositoryError::Runtime(RuntimeError::Graph(format!(
230                    "create graph cannot remove node {}",
231                    node.entity
232                ))));
233            }
234        }
235
236        let descriptor = self
237            .repository
238            .metadata
239            .context
240            .require_entity(&node.entity)
241            .map_err(RepositoryError::Runtime)?;
242
243        let mut one_relations = Vec::new();
244        let mut many_relations = Vec::new();
245        for (name, children) in std::mem::take(&mut node.relations) {
246            let relation = descriptor.relation_by_name(&name).ok_or_else(|| {
247                RepositoryError::Runtime(RuntimeError::MissingRelation {
248                    entity: node.entity.clone(),
249                    relation: name.clone(),
250                })
251            })?;
252            if relation.many {
253                many_relations.push((name, relation.clone(), children));
254            } else {
255                one_relations.push((name, relation.clone(), children));
256            }
257        }
258
259        for (name, relation, children) in one_relations {
260            if children.len() > 1 {
261                return Err(RepositoryError::Runtime(RuntimeError::Graph(format!(
262                    "relation {}.{} expects one child, got {}",
263                    node.entity,
264                    name,
265                    children.len()
266                ))));
267            }
268            let mut saved_children = Vec::new();
269            for child in children {
270                ensure_relation_target(&node.entity, &name, &relation.target_entity, &child)?;
271                let child_repo = self.scoped_repository(child.entity.clone());
272                let saved_child = child_repo.insert_graph_node(child)?;
273                if relation.attach {
274                    let foreign_value = saved_child
275                        .values
276                        .get(&relation.foreign_key)
277                        .cloned()
278                        .ok_or_else(|| {
279                            RepositoryError::Runtime(RuntimeError::Graph(format!(
280                                "saved child {} missing foreign key {} for relation {}.{}",
281                                relation.target_entity, relation.foreign_key, node.entity, name
282                            )))
283                        })?;
284                    node.values
285                        .insert(relation.local_key.clone(), foreign_value);
286                }
287                saved_children.push(saved_child);
288            }
289            node.relations.insert(name, saved_children);
290        }
291
292        let command = self
293            .prepare_insert_command(&InsertCommand {
294                entity: node.entity.clone(),
295                values: node.values.clone(),
296            })
297            .map_err(RepositoryError::Runtime)?;
298        self.execute_prepared_insert(command.clone())?;
299        node.values = command.values;
300
301        for (name, relation, children) in many_relations {
302            let local_value = node
303                .values
304                .get(&relation.local_key)
305                .cloned()
306                .ok_or_else(|| {
307                    RepositoryError::Runtime(RuntimeError::Graph(format!(
308                        "parent {} missing local key {} for relation {}",
309                        node.entity, relation.local_key, name
310                    )))
311                })?;
312            let mut saved_children = Vec::new();
313            for mut child in children {
314                ensure_relation_target(&node.entity, &name, &relation.target_entity, &child)?;
315                if relation.attach {
316                    child
317                        .values
318                        .insert(relation.foreign_key.clone(), local_value.clone());
319                }
320                let child_repo = self.scoped_repository(child.entity.clone());
321                saved_children.push(child_repo.insert_graph_node(child)?);
322            }
323            node.relations.insert(name, saved_children);
324        }
325
326        Ok(node)
327    }
328
329    fn upsert_graph_node(
330        &self,
331        mut node: GraphNode,
332    ) -> Result<GraphNode, RepositoryError<E::Error>> {
333        match node.operation {
334            GraphOperation::Upsert => {}
335            GraphOperation::Reference => return self.validate_reference_node(node),
336            GraphOperation::Remove => {
337                self.validate_remove_node(&node)?;
338                self.delete_graph_node(&node)?;
339                return Ok(node);
340            }
341        }
342
343        let descriptor = self
344            .repository
345            .metadata
346            .context
347            .require_entity(&node.entity)
348            .map_err(RepositoryError::Runtime)?;
349        let Some(id_property) = descriptor.id_property() else {
350            return Err(RepositoryError::Runtime(RuntimeError::Graph(format!(
351                "entity {} has no id property for graph upsert",
352                node.entity
353            ))));
354        };
355        let Some(id) = node
356            .values
357            .get(&id_property.name)
358            .filter(|value| !is_unassigned_id_value(value))
359            .cloned()
360        else {
361            return self.insert_graph_node(node);
362        };
363
364        if self
365            .fetch_graph_current_row(&node.entity, &id_property.name, &id)?
366            .is_none()
367        {
368            return self.insert_graph_node(node);
369        }
370
371        let mut one_relations = Vec::new();
372        let mut many_relations = Vec::new();
373        for (name, children) in std::mem::take(&mut node.relations) {
374            let relation = descriptor.relation_by_name(&name).ok_or_else(|| {
375                RepositoryError::Runtime(RuntimeError::MissingRelation {
376                    entity: node.entity.clone(),
377                    relation: name.clone(),
378                })
379            })?;
380            if relation.many {
381                many_relations.push((name, relation.clone(), children));
382            } else {
383                one_relations.push((name, relation.clone(), children));
384            }
385        }
386
387        for (name, relation, children) in one_relations {
388            if children.len() > 1 {
389                return Err(RepositoryError::Runtime(RuntimeError::Graph(format!(
390                    "relation {}.{} expects one child, got {}",
391                    node.entity,
392                    name,
393                    children.len()
394                ))));
395            }
396            let mut saved_children = Vec::new();
397            for child in children {
398                ensure_relation_target(&node.entity, &name, &relation.target_entity, &child)?;
399                let child_repo = self.scoped_repository(child.entity.clone());
400                let saved_child = child_repo.upsert_graph_node(child)?;
401                if relation.attach {
402                    let foreign_value = saved_child
403                        .values
404                        .get(&relation.foreign_key)
405                        .cloned()
406                        .ok_or_else(|| {
407                            RepositoryError::Runtime(RuntimeError::Graph(format!(
408                                "saved child {} missing foreign key {} for relation {}.{}",
409                                relation.target_entity, relation.foreign_key, node.entity, name
410                            )))
411                        })?;
412                    node.values
413                        .insert(relation.local_key.clone(), foreign_value);
414                }
415                saved_children.push(saved_child);
416            }
417            node.relations.insert(name, saved_children);
418        }
419
420        let update = self.graph_update_command(&node, descriptor, id_property, &id);
421        if !update.values.is_empty() || update.expected_version.is_some() {
422            let prepared_update = self
423                .prepare_update_command(&update)
424                .map_err(RepositoryError::Runtime)?;
425            self.execute_prepared_update(prepared_update.clone())?;
426            for (field, value) in &prepared_update.values {
427                node.values.insert(field.clone(), value.clone());
428            }
429            if let Some(version_property) = descriptor.version_property() {
430                if let Some(expected_version) = prepared_update.expected_version {
431                    node.values.insert(
432                        version_property.name.clone(),
433                        Value::I64(expected_version + 1),
434                    );
435                }
436            }
437        }
438
439        for (name, relation, children) in many_relations {
440            let local_value = node
441                .values
442                .get(&relation.local_key)
443                .cloned()
444                .ok_or_else(|| {
445                    RepositoryError::Runtime(RuntimeError::Graph(format!(
446                        "parent {} missing local key {} for relation {}",
447                        node.entity, relation.local_key, name
448                    )))
449                })?;
450            let child_repo = self.scoped_repository(relation.target_entity.clone());
451            let existing_children = child_repo.fetch_graph_children(
452                &relation.target_entity,
453                &relation.foreign_key,
454                &local_value,
455            )?;
456            let child_descriptor = self
457                .repository
458                .metadata
459                .context
460                .require_entity(&relation.target_entity)
461                .map_err(RepositoryError::Runtime)?;
462            let child_id_property = child_descriptor.id_property().ok_or_else(|| {
463                RepositoryError::Runtime(RuntimeError::Graph(format!(
464                    "entity {} has no id property for graph diff",
465                    relation.target_entity
466                )))
467            })?;
468            let mut existing_by_id = BTreeMap::new();
469            for child in existing_children {
470                if let Some(id) = child.get(&child_id_property.name) {
471                    existing_by_id.insert(graph_identity_key(id), child);
472                }
473            }
474
475            let mut seen = std::collections::BTreeSet::new();
476            let mut saved_children = Vec::new();
477            for mut child in children {
478                ensure_relation_target(&node.entity, &name, &relation.target_entity, &child)?;
479                if relation.attach && child.operation != GraphOperation::Reference {
480                    child
481                        .values
482                        .insert(relation.foreign_key.clone(), local_value.clone());
483                }
484                if let Some(child_id) = child
485                    .values
486                    .get(&child_id_property.name)
487                    .filter(|value| !is_unassigned_id_value(value))
488                {
489                    let key = graph_identity_key(child_id);
490                    if !seen.insert(key.clone()) {
491                        return Err(RepositoryError::Runtime(RuntimeError::Graph(format!(
492                            "duplicate child id {key} in relation {}.{}",
493                            node.entity, name
494                        ))));
495                    }
496                }
497                saved_children.push(child_repo.upsert_graph_node(child)?);
498            }
499
500            if relation.delete_missing {
501                for (id_key, existing) in existing_by_id {
502                    if seen.contains(&id_key) {
503                        continue;
504                    }
505                    let Some(existing_id) = existing.get(&child_id_property.name).cloned() else {
506                        continue;
507                    };
508                    let mut delete =
509                        DeleteCommand::new(relation.target_entity.clone(), existing_id);
510                    if let Some(version) = graph_record_version(&existing, child_descriptor) {
511                        delete = delete.expected_version(version);
512                    }
513                    child_repo.delete(&delete)?;
514                }
515            }
516
517            node.relations.insert(name, saved_children);
518        }
519
520        Ok(node)
521    }
522
523    fn validate_reference_node(
524        &self,
525        node: GraphNode,
526    ) -> Result<GraphNode, RepositoryError<E::Error>> {
527        if !node.relations.is_empty() {
528            return Err(RepositoryError::Runtime(RuntimeError::Graph(format!(
529                "reference node {} cannot contain child relations",
530                node.entity
531            ))));
532        }
533        let descriptor = self
534            .repository
535            .metadata
536            .context
537            .require_entity(&node.entity)
538            .map_err(RepositoryError::Runtime)?;
539        let id_property = descriptor.id_property().ok_or_else(|| {
540            RepositoryError::Runtime(RuntimeError::Graph(format!(
541                "entity {} has no id property for graph reference",
542                node.entity
543            )))
544        })?;
545        let id = node
546            .values
547            .get(&id_property.name)
548            .filter(|value| !is_unassigned_id_value(value))
549            .cloned()
550            .ok_or_else(|| {
551                RepositoryError::Runtime(RuntimeError::Graph(format!(
552                    "reference node {} missing id property {}",
553                    node.entity, id_property.name
554                )))
555            })?;
556
557        for field in node.values.keys() {
558            if field == &id_property.name {
559                continue;
560            }
561            if descriptor
562                .version_property()
563                .map(|property| field == &property.name)
564                .unwrap_or(false)
565            {
566                continue;
567            }
568            return Err(RepositoryError::Runtime(RuntimeError::Graph(format!(
569                "reference node {} cannot carry mutable field {}",
570                node.entity, field
571            ))));
572        }
573
574        let current = self
575            .fetch_graph_current_row(&node.entity, &id_property.name, &id)?
576            .ok_or_else(|| {
577                RepositoryError::Runtime(RuntimeError::Graph(format!(
578                    "reference node {}({}) does not exist",
579                    node.entity,
580                    graph_identity_key(&id)
581                )))
582            })?;
583
584        if let Some(version_property) = descriptor.version_property() {
585            if let Some(Value::I64(existing_version)) = current.get(&version_property.name) {
586                if *existing_version < 0 {
587                    return Err(RepositoryError::Runtime(RuntimeError::Graph(format!(
588                        "reference node {}({}) is deleted",
589                        node.entity,
590                        graph_identity_key(&id)
591                    ))));
592                }
593                if let Some(Value::I64(expected_version)) = node.values.get(&version_property.name)
594                {
595                    if expected_version != existing_version {
596                        return Err(RepositoryError::Runtime(
597                            RuntimeError::OptimisticLockConflict {
598                                entity: node.entity,
599                                id: graph_identity_key(&id),
600                            },
601                        ));
602                    }
603                }
604            }
605        }
606
607        Ok(GraphNode {
608            entity: node.entity,
609            values: current,
610            relations: BTreeMap::new(),
611            operation: GraphOperation::Reference,
612        })
613    }
614
615    fn validate_remove_node(&self, node: &GraphNode) -> Result<(), RepositoryError<E::Error>> {
616        if !node.relations.is_empty() {
617            return Err(RepositoryError::Runtime(RuntimeError::Graph(format!(
618                "remove node {} cannot contain child relations",
619                node.entity
620            ))));
621        }
622        let descriptor = self
623            .repository
624            .metadata
625            .context
626            .require_entity(&node.entity)
627            .map_err(RepositoryError::Runtime)?;
628        let id_property = descriptor.id_property().ok_or_else(|| {
629            RepositoryError::Runtime(RuntimeError::Graph(format!(
630                "entity {} has no id property for graph remove",
631                node.entity
632            )))
633        })?;
634        let id = node
635            .values
636            .get(&id_property.name)
637            .filter(|value| !is_unassigned_id_value(value))
638            .cloned()
639            .ok_or_else(|| {
640                RepositoryError::Runtime(RuntimeError::Graph(format!(
641                    "remove node {} missing id property {}",
642                    node.entity, id_property.name
643                )))
644            })?;
645        let current = self
646            .fetch_graph_current_row(&node.entity, &id_property.name, &id)?
647            .ok_or_else(|| {
648                RepositoryError::Runtime(RuntimeError::Graph(format!(
649                    "remove node {}({}) does not exist",
650                    node.entity,
651                    graph_identity_key(&id)
652                )))
653            })?;
654        if let Some(version_property) = descriptor.version_property() {
655            if let Some(Value::I64(existing_version)) = current.get(&version_property.name) {
656                if *existing_version < 0 {
657                    return Err(RepositoryError::Runtime(RuntimeError::Graph(format!(
658                        "remove node {}({}) is already deleted",
659                        node.entity,
660                        graph_identity_key(&id)
661                    ))));
662                }
663            }
664        }
665        Ok(())
666    }
667
668    fn graph_node_from_record(
669        &self,
670        entity: &str,
671        record: Record,
672    ) -> Result<GraphNode, RuntimeError> {
673        let descriptor = self.repository.metadata.context.require_entity(entity)?;
674        let mut node = GraphNode::new(entity);
675
676        for (field, value) in record {
677            let Some(relation) = descriptor.relation_by_name(&field) else {
678                node.values.insert(field, value);
679                continue;
680            };
681
682            match value {
683                Value::Null => {
684                    node.relations.entry(field).or_default();
685                }
686                Value::Object(record) => {
687                    let child = self.graph_node_from_record(&relation.target_entity, record)?;
688                    node.relations.entry(field).or_default().push(child);
689                }
690                Value::List(values) => {
691                    let children = node.relations.entry(field.clone()).or_default();
692                    for value in values {
693                        let Value::Object(record) = value else {
694                            return Err(RuntimeError::Graph(format!(
695                                "relation {}.{} expects object children, got {:?}",
696                                entity, field, value
697                            )));
698                        };
699                        children
700                            .push(self.graph_node_from_record(&relation.target_entity, record)?);
701                    }
702                }
703                other => {
704                    return Err(RuntimeError::Graph(format!(
705                        "relation {}.{} expects object/list/null, got {:?}",
706                        entity, field, other
707                    )));
708                }
709            }
710        }
711
712        Ok(node)
713    }
714
715    fn graph_update_command(
716        &self,
717        node: &GraphNode,
718        descriptor: &EntityDescriptor,
719        id_property: &PropertyDescriptor,
720        id: &Value,
721    ) -> UpdateCommand {
722        let mut command = UpdateCommand::new(node.entity.clone(), id.clone());
723        if let Some(version_property) = descriptor.version_property() {
724            if let Some(Value::I64(version)) = node.values.get(&version_property.name) {
725                command = command.expected_version(*version);
726            }
727        }
728        for property in descriptor.properties.iter().filter(|property| {
729            !property.is_id && !property.is_version && node.values.contains_key(&property.name)
730        }) {
731            if property.name == id_property.name {
732                continue;
733            }
734            if let Some(value) = node.values.get(&property.name) {
735                command.values.insert(property.name.clone(), value.clone());
736            }
737        }
738        command
739    }
740
741    fn delete_graph_node(&self, node: &GraphNode) -> Result<u64, RepositoryError<E::Error>> {
742        let descriptor = self
743            .repository
744            .metadata
745            .context
746            .require_entity(&node.entity)
747            .map_err(RepositoryError::Runtime)?;
748        let id_property = descriptor.id_property().ok_or_else(|| {
749            RepositoryError::Runtime(RuntimeError::Graph(format!(
750                "entity {} has no id property for graph remove",
751                node.entity
752            )))
753        })?;
754        let id = node
755            .values
756            .get(&id_property.name)
757            .filter(|value| !is_unassigned_id_value(value))
758            .cloned()
759            .ok_or_else(|| {
760                RepositoryError::Runtime(RuntimeError::Graph(format!(
761                    "remove node {} missing id property {}",
762                    node.entity, id_property.name
763                )))
764            })?;
765        let mut delete = DeleteCommand::new(node.entity.clone(), id);
766        if let Some(version_property) = descriptor.version_property() {
767            if let Some(Value::I64(version)) = node.values.get(&version_property.name) {
768                delete = delete.expected_version(*version);
769            }
770        }
771        self.delete(&delete)
772    }
773
774    fn fetch_graph_current_row(
775        &self,
776        entity: &str,
777        id_property: &str,
778        id: &Value,
779    ) -> Result<Option<Record>, RepositoryError<E::Error>> {
780        let mut rows = self
781            .scoped_repository(entity.to_owned())
782            .fetch_all(&SelectQuery::new(entity).filter(Expr::eq(id_property, id.clone())))?;
783        Ok(rows.pop())
784    }
785
786    fn fetch_graph_children(
787        &self,
788        entity: &str,
789        foreign_key: &str,
790        parent_value: &Value,
791    ) -> Result<Vec<Record>, RepositoryError<E::Error>> {
792        self.scoped_repository(entity.to_owned()).fetch_all(
793            &SelectQuery::new(entity).filter(Expr::eq(foreign_key, parent_value.clone())),
794        )
795    }
796}