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        }
181        let update_fields = if is_update {
182            let mut excluded = Vec::new();
183            if let Some(id_property) = id_property.as_ref() {
184                excluded.push(id_property.name.clone());
185            }
186            if let Some(version_property) = descriptor.version_property() {
187                excluded.push(version_property.name.clone());
188            }
189            sorted_update_fields(&node.values, excluded)
190        } else {
191            Vec::new()
192        };
193        plan.push(
194            node.entity.clone(),
195            if is_update {
196                GraphMutationKind::Update
197            } else {
198                GraphMutationKind::Create
199            },
200            node.values.clone(),
201            update_fields,
202        );
203
204        for (name, children) in &mut node.relations {
205            let relation = descriptor.relation_by_name(name).ok_or_else(|| {
206                RepositoryError::Runtime(RuntimeError::MissingRelation {
207                    entity: node.entity.clone(),
208                    relation: name.clone(),
209                })
210            })?;
211            let child_repo = self.scoped_repository(relation.target_entity.clone());
212            for child in children {
213                ensure_relation_target(&node.entity, name, &relation.target_entity, child)?;
214                child_repo.collect_graph_plan(child, plan)?;
215            }
216        }
217        Ok(())
218    }
219
220    fn insert_graph_node(
221        &self,
222        mut node: GraphNode,
223    ) -> Result<GraphNode, RepositoryError<E::Error>> {
224        match node.operation {
225            GraphOperation::Upsert => {}
226            GraphOperation::Reference => return self.validate_reference_node(node),
227            GraphOperation::Remove => {
228                return Err(RepositoryError::Runtime(RuntimeError::Graph(format!(
229                    "create graph cannot remove node {}",
230                    node.entity
231                ))));
232            }
233        }
234
235        let descriptor = self
236            .repository
237            .metadata
238            .context
239            .require_entity(&node.entity)
240            .map_err(RepositoryError::Runtime)?;
241
242        let mut one_relations = Vec::new();
243        let mut many_relations = Vec::new();
244        for (name, children) in std::mem::take(&mut node.relations) {
245            let relation = descriptor.relation_by_name(&name).ok_or_else(|| {
246                RepositoryError::Runtime(RuntimeError::MissingRelation {
247                    entity: node.entity.clone(),
248                    relation: name.clone(),
249                })
250            })?;
251            if relation.many {
252                many_relations.push((name, relation.clone(), children));
253            } else {
254                one_relations.push((name, relation.clone(), children));
255            }
256        }
257
258        for (name, relation, children) in one_relations {
259            if children.len() > 1 {
260                return Err(RepositoryError::Runtime(RuntimeError::Graph(format!(
261                    "relation {}.{} expects one child, got {}",
262                    node.entity,
263                    name,
264                    children.len()
265                ))));
266            }
267            let mut saved_children = Vec::new();
268            for child in children {
269                ensure_relation_target(&node.entity, &name, &relation.target_entity, &child)?;
270                let child_repo = self.scoped_repository(child.entity.clone());
271                let saved_child = child_repo.insert_graph_node(child)?;
272                if relation.attach {
273                    let foreign_value = saved_child
274                        .values
275                        .get(&relation.foreign_key)
276                        .cloned()
277                        .ok_or_else(|| {
278                            RepositoryError::Runtime(RuntimeError::Graph(format!(
279                                "saved child {} missing foreign key {} for relation {}.{}",
280                                relation.target_entity, relation.foreign_key, node.entity, name
281                            )))
282                        })?;
283                    node.values
284                        .insert(relation.local_key.clone(), foreign_value);
285                }
286                saved_children.push(saved_child);
287            }
288            node.relations.insert(name, saved_children);
289        }
290
291        let command = self
292            .prepare_insert_command(&InsertCommand {
293                entity: node.entity.clone(),
294                values: node.values.clone(),
295            })
296            .map_err(RepositoryError::Runtime)?;
297        self.execute_prepared_insert(command.clone())?;
298        node.values = command.values;
299
300        for (name, relation, children) in many_relations {
301            let local_value = node
302                .values
303                .get(&relation.local_key)
304                .cloned()
305                .ok_or_else(|| {
306                    RepositoryError::Runtime(RuntimeError::Graph(format!(
307                        "parent {} missing local key {} for relation {}",
308                        node.entity, relation.local_key, name
309                    )))
310                })?;
311            let mut saved_children = Vec::new();
312            for mut child in children {
313                ensure_relation_target(&node.entity, &name, &relation.target_entity, &child)?;
314                if relation.attach {
315                    child
316                        .values
317                        .insert(relation.foreign_key.clone(), local_value.clone());
318                }
319                let child_repo = self.scoped_repository(child.entity.clone());
320                saved_children.push(child_repo.insert_graph_node(child)?);
321            }
322            node.relations.insert(name, saved_children);
323        }
324
325        Ok(node)
326    }
327
328    fn upsert_graph_node(
329        &self,
330        mut node: GraphNode,
331    ) -> Result<GraphNode, RepositoryError<E::Error>> {
332        match node.operation {
333            GraphOperation::Upsert => {}
334            GraphOperation::Reference => return self.validate_reference_node(node),
335            GraphOperation::Remove => {
336                self.validate_remove_node(&node)?;
337                self.delete_graph_node(&node)?;
338                return Ok(node);
339            }
340        }
341
342        let descriptor = self
343            .repository
344            .metadata
345            .context
346            .require_entity(&node.entity)
347            .map_err(RepositoryError::Runtime)?;
348        let Some(id_property) = descriptor.id_property() else {
349            return Err(RepositoryError::Runtime(RuntimeError::Graph(format!(
350                "entity {} has no id property for graph upsert",
351                node.entity
352            ))));
353        };
354        let Some(id) = node
355            .values
356            .get(&id_property.name)
357            .filter(|value| !matches!(value, Value::Null))
358            .cloned()
359        else {
360            return self.insert_graph_node(node);
361        };
362
363        if self
364            .fetch_graph_current_row(&node.entity, &id_property.name, &id)?
365            .is_none()
366        {
367            return self.insert_graph_node(node);
368        }
369
370        let mut one_relations = Vec::new();
371        let mut many_relations = Vec::new();
372        for (name, children) in std::mem::take(&mut node.relations) {
373            let relation = descriptor.relation_by_name(&name).ok_or_else(|| {
374                RepositoryError::Runtime(RuntimeError::MissingRelation {
375                    entity: node.entity.clone(),
376                    relation: name.clone(),
377                })
378            })?;
379            if relation.many {
380                many_relations.push((name, relation.clone(), children));
381            } else {
382                one_relations.push((name, relation.clone(), children));
383            }
384        }
385
386        for (name, relation, children) in one_relations {
387            if children.len() > 1 {
388                return Err(RepositoryError::Runtime(RuntimeError::Graph(format!(
389                    "relation {}.{} expects one child, got {}",
390                    node.entity,
391                    name,
392                    children.len()
393                ))));
394            }
395            let mut saved_children = Vec::new();
396            for child in children {
397                ensure_relation_target(&node.entity, &name, &relation.target_entity, &child)?;
398                let child_repo = self.scoped_repository(child.entity.clone());
399                let saved_child = child_repo.upsert_graph_node(child)?;
400                if relation.attach {
401                    let foreign_value = saved_child
402                        .values
403                        .get(&relation.foreign_key)
404                        .cloned()
405                        .ok_or_else(|| {
406                            RepositoryError::Runtime(RuntimeError::Graph(format!(
407                                "saved child {} missing foreign key {} for relation {}.{}",
408                                relation.target_entity, relation.foreign_key, node.entity, name
409                            )))
410                        })?;
411                    node.values
412                        .insert(relation.local_key.clone(), foreign_value);
413                }
414                saved_children.push(saved_child);
415            }
416            node.relations.insert(name, saved_children);
417        }
418
419        let update = self.graph_update_command(&node, descriptor, id_property, &id);
420        if !update.values.is_empty() || update.expected_version.is_some() {
421            let prepared_update = self
422                .prepare_update_command(&update)
423                .map_err(RepositoryError::Runtime)?;
424            self.execute_prepared_update(prepared_update.clone())?;
425            for (field, value) in &prepared_update.values {
426                node.values.insert(field.clone(), value.clone());
427            }
428            if let Some(version_property) = descriptor.version_property() {
429                if let Some(expected_version) = prepared_update.expected_version {
430                    node.values.insert(
431                        version_property.name.clone(),
432                        Value::I64(expected_version + 1),
433                    );
434                }
435            }
436        }
437
438        for (name, relation, children) in many_relations {
439            let local_value = node
440                .values
441                .get(&relation.local_key)
442                .cloned()
443                .ok_or_else(|| {
444                    RepositoryError::Runtime(RuntimeError::Graph(format!(
445                        "parent {} missing local key {} for relation {}",
446                        node.entity, relation.local_key, name
447                    )))
448                })?;
449            let child_repo = self.scoped_repository(relation.target_entity.clone());
450            let existing_children = child_repo.fetch_graph_children(
451                &relation.target_entity,
452                &relation.foreign_key,
453                &local_value,
454            )?;
455            let child_descriptor = self
456                .repository
457                .metadata
458                .context
459                .require_entity(&relation.target_entity)
460                .map_err(RepositoryError::Runtime)?;
461            let child_id_property = child_descriptor.id_property().ok_or_else(|| {
462                RepositoryError::Runtime(RuntimeError::Graph(format!(
463                    "entity {} has no id property for graph diff",
464                    relation.target_entity
465                )))
466            })?;
467            let mut existing_by_id = BTreeMap::new();
468            for child in existing_children {
469                if let Some(id) = child.get(&child_id_property.name) {
470                    existing_by_id.insert(graph_identity_key(id), child);
471                }
472            }
473
474            let mut seen = std::collections::BTreeSet::new();
475            let mut saved_children = Vec::new();
476            for mut child in children {
477                ensure_relation_target(&node.entity, &name, &relation.target_entity, &child)?;
478                if relation.attach && child.operation != GraphOperation::Reference {
479                    child
480                        .values
481                        .insert(relation.foreign_key.clone(), local_value.clone());
482                }
483                if let Some(child_id) = child
484                    .values
485                    .get(&child_id_property.name)
486                    .filter(|value| !is_unassigned_id_value(value))
487                {
488                    let key = graph_identity_key(child_id);
489                    if !seen.insert(key.clone()) {
490                        return Err(RepositoryError::Runtime(RuntimeError::Graph(format!(
491                            "duplicate child id {key} in relation {}.{}",
492                            node.entity, name
493                        ))));
494                    }
495                }
496                saved_children.push(child_repo.upsert_graph_node(child)?);
497            }
498
499            if relation.delete_missing {
500                for (id_key, existing) in existing_by_id {
501                    if seen.contains(&id_key) {
502                        continue;
503                    }
504                    let Some(existing_id) = existing.get(&child_id_property.name).cloned() else {
505                        continue;
506                    };
507                    let mut delete =
508                        DeleteCommand::new(relation.target_entity.clone(), existing_id);
509                    if let Some(version) = graph_record_version(&existing, child_descriptor) {
510                        delete = delete.expected_version(version);
511                    }
512                    child_repo.delete(&delete)?;
513                }
514            }
515
516            node.relations.insert(name, saved_children);
517        }
518
519        Ok(node)
520    }
521
522    fn validate_reference_node(
523        &self,
524        node: GraphNode,
525    ) -> Result<GraphNode, RepositoryError<E::Error>> {
526        if !node.relations.is_empty() {
527            return Err(RepositoryError::Runtime(RuntimeError::Graph(format!(
528                "reference node {} cannot contain child relations",
529                node.entity
530            ))));
531        }
532        let descriptor = self
533            .repository
534            .metadata
535            .context
536            .require_entity(&node.entity)
537            .map_err(RepositoryError::Runtime)?;
538        let id_property = descriptor.id_property().ok_or_else(|| {
539            RepositoryError::Runtime(RuntimeError::Graph(format!(
540                "entity {} has no id property for graph reference",
541                node.entity
542            )))
543        })?;
544        let id = node
545            .values
546            .get(&id_property.name)
547            .filter(|value| !is_unassigned_id_value(value))
548            .cloned()
549            .ok_or_else(|| {
550                RepositoryError::Runtime(RuntimeError::Graph(format!(
551                    "reference node {} missing id property {}",
552                    node.entity, id_property.name
553                )))
554            })?;
555
556        for field in node.values.keys() {
557            if field == &id_property.name {
558                continue;
559            }
560            if descriptor
561                .version_property()
562                .map(|property| field == &property.name)
563                .unwrap_or(false)
564            {
565                continue;
566            }
567            return Err(RepositoryError::Runtime(RuntimeError::Graph(format!(
568                "reference node {} cannot carry mutable field {}",
569                node.entity, field
570            ))));
571        }
572
573        let current = self
574            .fetch_graph_current_row(&node.entity, &id_property.name, &id)?
575            .ok_or_else(|| {
576                RepositoryError::Runtime(RuntimeError::Graph(format!(
577                    "reference node {}({}) does not exist",
578                    node.entity,
579                    graph_identity_key(&id)
580                )))
581            })?;
582
583        if let Some(version_property) = descriptor.version_property() {
584            if let Some(Value::I64(existing_version)) = current.get(&version_property.name) {
585                if *existing_version < 0 {
586                    return Err(RepositoryError::Runtime(RuntimeError::Graph(format!(
587                        "reference node {}({}) is deleted",
588                        node.entity,
589                        graph_identity_key(&id)
590                    ))));
591                }
592                if let Some(Value::I64(expected_version)) = node.values.get(&version_property.name)
593                {
594                    if expected_version != existing_version {
595                        return Err(RepositoryError::Runtime(
596                            RuntimeError::OptimisticLockConflict {
597                                entity: node.entity,
598                                id: graph_identity_key(&id),
599                            },
600                        ));
601                    }
602                }
603            }
604        }
605
606        Ok(GraphNode {
607            entity: node.entity,
608            values: current,
609            relations: BTreeMap::new(),
610            operation: GraphOperation::Reference,
611        })
612    }
613
614    fn validate_remove_node(&self, node: &GraphNode) -> Result<(), RepositoryError<E::Error>> {
615        if !node.relations.is_empty() {
616            return Err(RepositoryError::Runtime(RuntimeError::Graph(format!(
617                "remove node {} cannot contain child relations",
618                node.entity
619            ))));
620        }
621        let descriptor = self
622            .repository
623            .metadata
624            .context
625            .require_entity(&node.entity)
626            .map_err(RepositoryError::Runtime)?;
627        let id_property = descriptor.id_property().ok_or_else(|| {
628            RepositoryError::Runtime(RuntimeError::Graph(format!(
629                "entity {} has no id property for graph remove",
630                node.entity
631            )))
632        })?;
633        let id = node
634            .values
635            .get(&id_property.name)
636            .filter(|value| !is_unassigned_id_value(value))
637            .cloned()
638            .ok_or_else(|| {
639                RepositoryError::Runtime(RuntimeError::Graph(format!(
640                    "remove node {} missing id property {}",
641                    node.entity, id_property.name
642                )))
643            })?;
644        let current = self
645            .fetch_graph_current_row(&node.entity, &id_property.name, &id)?
646            .ok_or_else(|| {
647                RepositoryError::Runtime(RuntimeError::Graph(format!(
648                    "remove node {}({}) does not exist",
649                    node.entity,
650                    graph_identity_key(&id)
651                )))
652            })?;
653        if let Some(version_property) = descriptor.version_property() {
654            if let Some(Value::I64(existing_version)) = current.get(&version_property.name) {
655                if *existing_version < 0 {
656                    return Err(RepositoryError::Runtime(RuntimeError::Graph(format!(
657                        "remove node {}({}) is already deleted",
658                        node.entity,
659                        graph_identity_key(&id)
660                    ))));
661                }
662            }
663        }
664        Ok(())
665    }
666
667    fn graph_node_from_record(
668        &self,
669        entity: &str,
670        record: Record,
671    ) -> Result<GraphNode, RuntimeError> {
672        let descriptor = self.repository.metadata.context.require_entity(entity)?;
673        let mut node = GraphNode::new(entity);
674
675        for (field, value) in record {
676            let Some(relation) = descriptor.relation_by_name(&field) else {
677                node.values.insert(field, value);
678                continue;
679            };
680
681            match value {
682                Value::Null => {
683                    node.relations.entry(field).or_default();
684                }
685                Value::Object(record) => {
686                    let child = self.graph_node_from_record(&relation.target_entity, record)?;
687                    node.relations.entry(field).or_default().push(child);
688                }
689                Value::List(values) => {
690                    let children = node.relations.entry(field.clone()).or_default();
691                    for value in values {
692                        let Value::Object(record) = value else {
693                            return Err(RuntimeError::Graph(format!(
694                                "relation {}.{} expects object children, got {:?}",
695                                entity, field, value
696                            )));
697                        };
698                        children
699                            .push(self.graph_node_from_record(&relation.target_entity, record)?);
700                    }
701                }
702                other => {
703                    return Err(RuntimeError::Graph(format!(
704                        "relation {}.{} expects object/list/null, got {:?}",
705                        entity, field, other
706                    )));
707                }
708            }
709        }
710
711        Ok(node)
712    }
713
714    fn graph_update_command(
715        &self,
716        node: &GraphNode,
717        descriptor: &EntityDescriptor,
718        id_property: &PropertyDescriptor,
719        id: &Value,
720    ) -> UpdateCommand {
721        let mut command = UpdateCommand::new(node.entity.clone(), id.clone());
722        if let Some(version_property) = descriptor.version_property() {
723            if let Some(Value::I64(version)) = node.values.get(&version_property.name) {
724                command = command.expected_version(*version);
725            }
726        }
727        for property in descriptor.properties.iter().filter(|property| {
728            !property.is_id && !property.is_version && node.values.contains_key(&property.name)
729        }) {
730            if property.name == id_property.name {
731                continue;
732            }
733            if let Some(value) = node.values.get(&property.name) {
734                command.values.insert(property.name.clone(), value.clone());
735            }
736        }
737        command
738    }
739
740    fn delete_graph_node(&self, node: &GraphNode) -> Result<u64, RepositoryError<E::Error>> {
741        let descriptor = self
742            .repository
743            .metadata
744            .context
745            .require_entity(&node.entity)
746            .map_err(RepositoryError::Runtime)?;
747        let id_property = descriptor.id_property().ok_or_else(|| {
748            RepositoryError::Runtime(RuntimeError::Graph(format!(
749                "entity {} has no id property for graph remove",
750                node.entity
751            )))
752        })?;
753        let id = node
754            .values
755            .get(&id_property.name)
756            .filter(|value| !is_unassigned_id_value(value))
757            .cloned()
758            .ok_or_else(|| {
759                RepositoryError::Runtime(RuntimeError::Graph(format!(
760                    "remove node {} missing id property {}",
761                    node.entity, id_property.name
762                )))
763            })?;
764        let mut delete = DeleteCommand::new(node.entity.clone(), id);
765        if let Some(version_property) = descriptor.version_property() {
766            if let Some(Value::I64(version)) = node.values.get(&version_property.name) {
767                delete = delete.expected_version(*version);
768            }
769        }
770        self.delete(&delete)
771    }
772
773    fn fetch_graph_current_row(
774        &self,
775        entity: &str,
776        id_property: &str,
777        id: &Value,
778    ) -> Result<Option<Record>, RepositoryError<E::Error>> {
779        let mut rows = self
780            .scoped_repository(entity.to_owned())
781            .fetch_all(&SelectQuery::new(entity).filter(Expr::eq(id_property, id.clone())))?;
782        Ok(rows.pop())
783    }
784
785    fn fetch_graph_children(
786        &self,
787        entity: &str,
788        foreign_key: &str,
789        parent_value: &Value,
790    ) -> Result<Vec<Record>, RepositoryError<E::Error>> {
791        self.scoped_repository(entity.to_owned()).fetch_all(
792            &SelectQuery::new(entity).filter(Expr::eq(foreign_key, parent_value.clone())),
793        )
794    }
795}
796
797fn is_unassigned_id_value(value: &Value) -> bool {
798    matches!(value, Value::Null | Value::U64(0) | Value::I64(0))
799}