Skip to main content

hawdb_executor/
store_adapter.rs

1// Copyright 2026 Nowledge
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Root storage adapter for executor-owned graph read contracts.
16
17use crate::store::{
18    AdjacencyReadMemory, GraphExecutionRead, GraphExecutionWrite, PrunedNodeScan,
19    PrunedRelationshipScan, ScanControl, SourceScanCandidateRow, SourceScanCandidateVisit,
20    SourceScanReadLimits,
21};
22use crate::QueryMemoryLease;
23use hawdb_core::error::Result;
24use hawdb_core::schema::Catalog;
25use hawdb_core::{LabelId, RelTypeId};
26use hawdb_plan_cypher::NodeProjectionAccess;
27use hawdb_storage::store::{GraphScanControl, GraphStore};
28use hawdb_storage::{
29    adjacency::AdjacencyDirection,
30    mutation::{GraphMutation, MutationLimits, MutationSummary, NodeSetAssignment, PropertyFilter},
31    NodeId, NodeRecord, ProjectedNodeRecord, RelId, RelRecord,
32};
33use std::collections::BTreeSet;
34
35fn to_store_control(control: ScanControl) -> GraphScanControl {
36    match control {
37        ScanControl::Continue => GraphScanControl::Continue,
38        ScanControl::Stop => GraphScanControl::Stop,
39    }
40}
41
42fn to_execution_control(control: GraphScanControl) -> ScanControl {
43    match control {
44        GraphScanControl::Continue => ScanControl::Continue,
45        GraphScanControl::Stop => ScanControl::Stop,
46    }
47}
48
49fn adapt_node_consumer(
50    consumer: &mut dyn FnMut(NodeRecord) -> Result<ScanControl>,
51    visit: impl FnOnce(&mut dyn FnMut(NodeRecord) -> GraphScanControl) -> Result<GraphScanControl>,
52) -> Result<ScanControl> {
53    let mut consumer_error = None;
54    let control = visit(&mut |node| match consumer(node) {
55        Ok(control) => to_store_control(control),
56        Err(error) => {
57            consumer_error = Some(error);
58            GraphScanControl::Stop
59        }
60    })?;
61    match consumer_error {
62        Some(error) => Err(error),
63        None => Ok(to_execution_control(control)),
64    }
65}
66
67impl hawdb_storage::read_view::GraphReadAllocation for QueryMemoryLease {
68    fn bytes(&self) -> usize {
69        self.bytes()
70    }
71    fn grow(&mut self, bytes: usize) -> hawdb_core::Result<()> {
72        QueryMemoryLease::grow(self, bytes)
73    }
74}
75
76impl GraphExecutionRead for GraphStore {
77    fn file_descriptor_context(&self) -> Option<hawdb_storage::file_descriptors::FileOpenContext> {
78        GraphStore::file_descriptor_context(self)
79    }
80
81    fn is_out_of_core(&self) -> bool {
82        GraphStore::is_out_of_core(self)
83    }
84
85    fn projected_node_owned_admitted(
86        &self,
87        id: NodeId,
88        required_properties: &BTreeSet<String>,
89        admit: &mut dyn FnMut(usize) -> Result<()>,
90    ) -> Result<Option<ProjectedNodeRecord>> {
91        GraphStore::projected_node_owned_admitted(self, id, required_properties, admit)
92    }
93
94    fn node_owned(&self, id: NodeId) -> Result<Option<NodeRecord>> {
95        GraphStore::node_owned(self, id)
96    }
97
98    fn node_with_allocation(
99        &self,
100        id: NodeId,
101        label_ids: Option<&[LabelId]>,
102        admit: &mut hawdb_storage::read_view::ControlledGraphReadAllocator<'_>,
103    ) -> Result<hawdb_storage::read_view::AdmittedNodeRead> {
104        GraphStore::node_with_allocation(self, id, label_ids, admit)
105    }
106
107    fn relationship_with_allocation(
108        &self,
109        id: RelId,
110        admit: &mut hawdb_storage::read_view::ControlledGraphReadAllocator<'_>,
111    ) -> Result<hawdb_storage::read_view::AdmittedRelationshipRead> {
112        GraphStore::relationship_with_allocation(self, id, admit)
113    }
114
115    fn scan_nodes_borrowed<'a>(
116        &'a self,
117        label_id: Option<LabelId>,
118    ) -> Box<dyn Iterator<Item = &'a NodeRecord> + 'a> {
119        Box::new(GraphStore::scan_nodes(self, label_id))
120    }
121
122    fn node_count_for_label(&self, label_id: Option<LabelId>) -> usize {
123        GraphStore::node_count_for_label(self, label_id)
124    }
125
126    fn relationship_count_for_type(&self, rel_type: Option<RelTypeId>) -> usize {
127        GraphStore::relationship_count_for_type(self, rel_type)
128    }
129
130    fn visit_nodes_owned(
131        &self,
132        label_id: Option<LabelId>,
133        consumer: &mut dyn FnMut(NodeRecord) -> Result<ScanControl>,
134    ) -> Result<ScanControl> {
135        GraphStore::try_visit_nodes_owned(self, label_id, |node| {
136            consumer(node).map(to_store_control)
137        })
138        .map(to_execution_control)
139    }
140
141    fn visit_relationships_with_allocation(
142        &self,
143        rel_type: Option<RelTypeId>,
144        admit: &mut hawdb_storage::read_view::ControlledGraphReadAllocator<'_>,
145        consumer: &mut dyn FnMut(
146            hawdb_storage::read_view::AdmittedRelationshipRecord,
147        ) -> Result<ScanControl>,
148    ) -> Result<ScanControl> {
149        GraphStore::visit_relationships_with_allocation(self, rel_type, admit, &mut |record| {
150            consumer(record).map(to_store_control)
151        })
152        .map(to_execution_control)
153    }
154
155    fn visit_nodes_with_allocation(
156        &self,
157        label_id: Option<LabelId>,
158        admit: &mut hawdb_storage::read_view::ControlledGraphReadAllocator<'_>,
159        consumer: &mut dyn FnMut(
160            hawdb_storage::read_view::AdmittedNodeRecord,
161        ) -> Result<ScanControl>,
162    ) -> Result<ScanControl> {
163        GraphStore::visit_nodes_with_allocation(self, label_id, admit, &mut |node| {
164            consumer(node).map(to_store_control)
165        })
166        .map(to_execution_control)
167    }
168
169    fn visit_nodes_by_access_with_allocation(
170        &self,
171        label_id: LabelId,
172        access: &NodeProjectionAccess,
173        admit: &mut hawdb_storage::read_view::ControlledGraphReadAllocator<'_>,
174        consumer: &mut dyn FnMut(
175            hawdb_storage::read_view::AdmittedNodeRecord,
176        ) -> Result<ScanControl>,
177    ) -> Result<ScanControl> {
178        GraphStore::visit_nodes_by_access_with_allocation(
179            self,
180            label_id,
181            access,
182            admit,
183            &mut |node| consumer(node).map(to_store_control),
184        )
185        .map(to_execution_control)
186    }
187
188    fn visit_projected_nodes_admitted(
189        &self,
190        label_id: Option<LabelId>,
191        properties: &BTreeSet<String>,
192        admit: &mut dyn FnMut(
193            usize,
194        )
195            -> Result<Box<dyn hawdb_storage::read_view::GraphReadAllocation>>,
196        consumer: &mut dyn FnMut(ProjectedNodeRecord) -> Result<ScanControl>,
197    ) -> Result<ScanControl> {
198        GraphStore::visit_projected_nodes_admitted(self, label_id, properties, admit, &mut |node| {
199            consumer(node).map(to_store_control)
200        })
201        .map(to_execution_control)
202    }
203
204    fn visit_projected_nodes_with_allocation(
205        &self,
206        label_id: Option<LabelId>,
207        properties: &BTreeSet<String>,
208        admit: &mut hawdb_storage::read_view::ControlledGraphReadAllocator<'_>,
209        consumer: &mut dyn FnMut(
210            hawdb_storage::read_view::AdmittedProjectedNode,
211        ) -> Result<ScanControl>,
212    ) -> Result<ScanControl> {
213        GraphStore::visit_projected_nodes_with_allocation(
214            self,
215            label_id,
216            properties,
217            admit,
218            &mut |node| consumer(node).map(to_store_control),
219        )
220        .map(to_execution_control)
221    }
222
223    fn visit_projected_nodes_by_access_admitted(
224        &self,
225        label_id: LabelId,
226        access: &NodeProjectionAccess,
227        properties: &BTreeSet<String>,
228        admit: &mut dyn FnMut(
229            usize,
230        )
231            -> Result<Box<dyn hawdb_storage::read_view::GraphReadAllocation>>,
232        consumer: &mut dyn FnMut(ProjectedNodeRecord) -> Result<ScanControl>,
233    ) -> Result<ScanControl> {
234        GraphStore::visit_projected_nodes_by_access_admitted(
235            self,
236            label_id,
237            access,
238            properties,
239            admit,
240            &mut |node| consumer(node).map(to_store_control),
241        )
242        .map(to_execution_control)
243    }
244
245    fn visit_projected_nodes_by_property_admitted(
246        &self,
247        label_id: LabelId,
248        property: &str,
249        values: &[hawdb_core::Value],
250        properties: &BTreeSet<String>,
251        admit: &mut dyn FnMut(
252            usize,
253        )
254            -> Result<Box<dyn hawdb_storage::read_view::GraphReadAllocation>>,
255        consumer: &mut dyn FnMut(ProjectedNodeRecord) -> Result<ScanControl>,
256    ) -> Result<ScanControl> {
257        GraphStore::visit_projected_nodes_by_property_admitted(
258            self,
259            label_id,
260            property,
261            values,
262            properties,
263            admit,
264            &mut |node| consumer(node).map(to_store_control),
265        )
266        .map(to_execution_control)
267    }
268
269    fn visit_projected_nodes_owned(
270        &self,
271        label_id: Option<LabelId>,
272        required_properties: &BTreeSet<String>,
273        consumer: &mut dyn FnMut(ProjectedNodeRecord) -> Result<ScanControl>,
274    ) -> Result<ScanControl> {
275        let mut consumer_error = None;
276        let control =
277            GraphStore::visit_projected_nodes_owned(self, label_id, required_properties, |node| {
278                match consumer(node) {
279                    Ok(control) => to_store_control(control),
280                    Err(error) => {
281                        consumer_error = Some(error);
282                        GraphScanControl::Stop
283                    }
284                }
285            })?;
286        match consumer_error {
287            Some(error) => Err(error),
288            None => Ok(to_execution_control(control)),
289        }
290    }
291
292    fn visit_projected_nodes_by_access_owned(
293        &self,
294        label_id: LabelId,
295        access: &NodeProjectionAccess,
296        required_properties: &BTreeSet<String>,
297        consumer: &mut dyn FnMut(ProjectedNodeRecord) -> Result<ScanControl>,
298    ) -> Result<ScanControl> {
299        let mut consumer_error = None;
300        let control = GraphStore::visit_projected_nodes_by_access_owned(
301            self,
302            label_id,
303            access,
304            required_properties,
305            |node| match consumer(node) {
306                Ok(control) => to_store_control(control),
307                Err(error) => {
308                    consumer_error = Some(error);
309                    GraphScanControl::Stop
310                }
311            },
312        )?;
313        match consumer_error {
314            Some(error) => Err(error),
315            None => Ok(to_execution_control(control)),
316        }
317    }
318
319    fn visit_projected_nodes_by_property_owned(
320        &self,
321        label_id: LabelId,
322        property: &str,
323        values: &[hawdb_core::Value],
324        required_properties: &BTreeSet<String>,
325        consumer: &mut dyn FnMut(ProjectedNodeRecord) -> Result<ScanControl>,
326    ) -> Result<ScanControl> {
327        let mut consumer_error = None;
328        let control = GraphStore::visit_projected_nodes_by_property_owned(
329            self,
330            label_id,
331            property,
332            values,
333            required_properties,
334            |node| match consumer(node) {
335                Ok(control) => to_store_control(control),
336                Err(error) => {
337                    consumer_error = Some(error);
338                    GraphScanControl::Stop
339                }
340            },
341        )?;
342        match consumer_error {
343            Some(error) => Err(error),
344            None => Ok(to_execution_control(control)),
345        }
346    }
347
348    fn visit_nodes_by_property_owned(
349        &self,
350        label_id: LabelId,
351        property: &str,
352        values: &[hawdb_core::Value],
353        consumer: &mut dyn FnMut(NodeRecord) -> Result<ScanControl>,
354    ) -> Result<ScanControl> {
355        let mut consumer_error = None;
356        let control =
357            GraphStore::visit_nodes_by_property_owned(self, label_id, property, values, |node| {
358                match consumer(node) {
359                    Ok(control) => to_store_control(control),
360                    Err(error) => {
361                        consumer_error = Some(error);
362                        GraphScanControl::Stop
363                    }
364                }
365            })?;
366        match consumer_error {
367            Some(error) => Err(error),
368            None => Ok(to_execution_control(control)),
369        }
370    }
371
372    fn visit_nodes_by_composite_property_owned(
373        &self,
374        label_id: LabelId,
375        predicates: &[(String, hawdb_core::Value)],
376        consumer: &mut dyn FnMut(NodeRecord) -> Result<ScanControl>,
377    ) -> Result<ScanControl> {
378        adapt_node_consumer(consumer, |consumer| {
379            GraphStore::visit_nodes_by_composite_property_owned(
380                self, label_id, predicates, consumer,
381            )
382        })
383    }
384
385    fn visit_nodes_by_composite_range_owned(
386        &self,
387        label_id: LabelId,
388        seek: &hawdb_plan_cypher::CompositeRangeSeek,
389        consumer: &mut dyn FnMut(NodeRecord) -> Result<ScanControl>,
390    ) -> Result<ScanControl> {
391        adapt_node_consumer(consumer, |consumer| {
392            GraphStore::visit_nodes_by_composite_range_owned(self, label_id, seek, consumer)
393        })
394    }
395
396    fn visit_nodes_by_property_range_owned(
397        &self,
398        label_id: LabelId,
399        property: &str,
400        lower: Option<&(hawdb_core::Value, bool)>,
401        upper: Option<&(hawdb_core::Value, bool)>,
402        consumer: &mut dyn FnMut(NodeRecord) -> Result<ScanControl>,
403    ) -> Result<ScanControl> {
404        adapt_node_consumer(consumer, |consumer| {
405            GraphStore::visit_nodes_by_property_range_owned(
406                self, label_id, property, lower, upper, consumer,
407            )
408        })
409    }
410
411    fn visit_nodes_by_full_text_property_owned(
412        &self,
413        label_id: LabelId,
414        property: &str,
415        query: &str,
416        consumer: &mut dyn FnMut(NodeRecord) -> Result<ScanControl>,
417    ) -> Result<ScanControl> {
418        adapt_node_consumer(consumer, |consumer| {
419            GraphStore::visit_nodes_by_full_text_property_owned(
420                self, label_id, property, query, consumer,
421            )
422        })
423    }
424
425    fn projected_graph_definition(
426        &self,
427        name: &str,
428    ) -> Option<hawdb_storage::projection::ProjectedGraphDefinition> {
429        GraphStore::projected_graph_definition(self, name).cloned()
430    }
431
432    fn visit_source_scan_candidates(
433        &self,
434        predicate: &hawdb_storage::scan::ScanPredicate,
435        limits: SourceScanReadLimits,
436        task_context: Option<&hawdb_core::RuntimeTaskContext>,
437        consumer: &mut dyn FnMut(SourceScanCandidateRow) -> Result<ScanControl>,
438    ) -> Result<SourceScanCandidateVisit> {
439        let mut consumer_error = None;
440        let visit = GraphStore::visit_published_source_scan_candidates_bounded(
441            self,
442            predicate,
443            hawdb_storage::store::SourceScanCandidateLimits::bounded(
444                limits.io_depth,
445                limits.max_coalesced_bytes,
446                limits.max_wave_bytes,
447                limits.max_live_candidate_bytes,
448            ),
449            task_context,
450            &mut |row| match consumer(SourceScanCandidateRow {
451                node_id: row.node_id,
452                properties: row.properties,
453            }) {
454                Ok(control) => Ok(to_store_control(control)),
455                Err(error) => {
456                    consumer_error = Some(error);
457                    Ok(hawdb_storage::store::GraphScanControl::Stop)
458                }
459            },
460        )?;
461        if let Some(error) = consumer_error {
462            return Err(error);
463        }
464        Ok(match visit {
465            hawdb_storage::store::SourceScanCandidateVisit::Rows {
466                graph_epoch,
467                skipped_segment_count,
468                report,
469                candidate_count,
470            } => SourceScanCandidateVisit::Rows {
471                graph_epoch,
472                skipped_segment_count,
473                report,
474                candidate_count,
475            },
476            hawdb_storage::store::SourceScanCandidateVisit::Fallback(reason) => {
477                SourceScanCandidateVisit::Fallback(reason)
478            }
479        })
480    }
481
482    fn visit_adjacent_relationships_owned(
483        &self,
484        node_id: NodeId,
485        rel_type: Option<RelTypeId>,
486        direction: AdjacencyDirection,
487        consumer: &mut dyn FnMut(RelRecord) -> Result<ScanControl>,
488    ) -> Result<ScanControl> {
489        GraphStore::try_visit_adjacent_relationships_owned(
490            self,
491            node_id,
492            rel_type,
493            direction,
494            |relationship| consumer(relationship).map(to_store_control),
495        )
496        .map(to_execution_control)
497    }
498
499    fn visit_ordered_adjacent_relationships_owned(
500        &self,
501        node_id: NodeId,
502        rel_type: Option<RelTypeId>,
503        direction: AdjacencyDirection,
504        memory: AdjacencyReadMemory<'_>,
505        consumer: &mut dyn FnMut(RelRecord) -> Result<ScanControl>,
506    ) -> Result<ScanControl> {
507        GraphExecutionRead::visit_ordered_adjacent_relationships_with_allocation(
508            self,
509            node_id,
510            rel_type,
511            direction,
512            memory,
513            &mut |bytes| memory.admit_node(bytes, 0, None).map(Some),
514            &mut |relationship| {
515                let (relationship, _allocation) = relationship.into_parts();
516                consumer(relationship)
517            },
518        )
519    }
520
521    fn visit_ordered_adjacent_relationships_with_allocation(
522        &self,
523        node_id: NodeId,
524        rel_type: Option<RelTypeId>,
525        direction: AdjacencyDirection,
526        memory: AdjacencyReadMemory<'_>,
527        admit: &mut hawdb_storage::read_view::ControlledGraphReadAllocator<'_>,
528        consumer: &mut dyn FnMut(
529            hawdb_storage::read_view::AdmittedRelationshipRecord,
530        ) -> Result<ScanControl>,
531    ) -> Result<ScanControl> {
532        GraphStore::visit_ordered_adjacent_relationships_with_allocation(
533            self,
534            node_id,
535            rel_type,
536            direction,
537            memory.budget_bytes,
538            admit,
539            |relationship| consumer(relationship).map(to_store_control),
540        )
541        .map(to_execution_control)
542    }
543
544    fn visit_filtered_ordered_relationships_with_allocation(
545        &self,
546        adjacency: (NodeId, Option<RelTypeId>, AdjacencyDirection),
547        filter: &PropertyFilter,
548        memory: AdjacencyReadMemory<'_>,
549        admit: &mut hawdb_storage::read_view::ControlledGraphReadAllocator<'_>,
550        consumer: &mut dyn FnMut(
551            hawdb_storage::read_view::AdmittedRelationshipRecord,
552        ) -> Result<ScanControl>,
553    ) -> Result<(ScanControl, Option<hawdb_storage::scan::ScanPruningReport>)> {
554        GraphStore::visit_filtered_ordered_relationships_with_allocation(
555            self,
556            adjacency,
557            filter,
558            memory.budget_bytes,
559            admit,
560            |row| consumer(row).map(to_store_control),
561        )
562        .map(|(control, report)| (to_execution_control(control), report))
563    }
564
565    fn visit_adjacent_relationships_with_filter_owned(
566        &self,
567        node_id: NodeId,
568        rel_type: Option<RelTypeId>,
569        direction: AdjacencyDirection,
570        filter: &PropertyFilter,
571        consumer: &mut dyn FnMut(RelRecord) -> Result<ScanControl>,
572    ) -> Result<(ScanControl, Option<hawdb_storage::scan::ScanPruningReport>)> {
573        let mut consumer_error = None;
574        let (control, report) = GraphStore::visit_adjacent_relationships_with_filter_owned(
575            self,
576            node_id,
577            rel_type,
578            direction,
579            filter,
580            |relationship| match consumer(relationship) {
581                Ok(control) => to_store_control(control),
582                Err(error) => {
583                    consumer_error = Some(error);
584                    GraphScanControl::Stop
585                }
586            },
587        )?;
588        match consumer_error {
589            Some(error) => Err(error),
590            None => Ok((to_execution_control(control), report)),
591        }
592    }
593
594    fn visit_ordered_adjacent_relationships_with_filter_owned(
595        &self,
596        node_id: NodeId,
597        rel_type: Option<RelTypeId>,
598        direction: AdjacencyDirection,
599        filter: &PropertyFilter,
600        memory: AdjacencyReadMemory<'_>,
601        consumer: &mut dyn FnMut(RelRecord) -> Result<ScanControl>,
602    ) -> Result<(ScanControl, Option<hawdb_storage::scan::ScanPruningReport>)> {
603        GraphStore::visit_filtered_ordered_relationships_with_allocation(
604            self,
605            (node_id, rel_type, direction),
606            filter,
607            memory.budget_bytes,
608            &mut |bytes| memory.admit_node(bytes, 0, None).map(Some),
609            |row| {
610                let (relationship, _allocation) = row.into_parts();
611                consumer(relationship).map(to_store_control)
612            },
613        )
614        .map(|(control, report)| (to_execution_control(control), report))
615    }
616
617    fn visit_relationships_owned(
618        &self,
619        rel_type: Option<RelTypeId>,
620        consumer: &mut dyn FnMut(RelRecord) -> Result<ScanControl>,
621    ) -> Result<ScanControl> {
622        GraphStore::try_visit_relationships_owned(self, rel_type, |relationship| {
623            consumer(relationship).map(to_store_control)
624        })
625        .map(to_execution_control)
626    }
627
628    fn scan_relationships_with_filter_pruning<'a>(
629        &'a self,
630        rel_type: Option<RelTypeId>,
631        filter: Option<&PropertyFilter>,
632    ) -> Result<PrunedRelationshipScan<'a>> {
633        let scan = GraphStore::scan_relationships_with_filter_pruning(self, rel_type, filter);
634        Ok(PrunedRelationshipScan {
635            relationships: Box::new(scan.relationships.into_iter().cloned()),
636            report: scan.report,
637        })
638    }
639
640    fn scan_nodes_with_filter_pruning<'a>(
641        &'a self,
642        catalog: &Catalog,
643        label_id: Option<LabelId>,
644        filter: Option<&PropertyFilter>,
645    ) -> Result<PrunedNodeScan<'a>> {
646        let scan = GraphStore::scan_nodes_with_filter_pruning(self, catalog, label_id, filter);
647        Ok(PrunedNodeScan {
648            nodes: Box::new(scan.nodes.into_iter().map(std::borrow::Cow::Borrowed)),
649            report: scan.report,
650        })
651    }
652
653    fn scan_nodes_with_filter_pruning_admitted<'a>(
654        &'a self,
655        catalog: &Catalog,
656        label_id: Option<LabelId>,
657        filter: Option<&PropertyFilter>,
658        admit: &mut hawdb_storage::read_view::GraphReadAllocator<'_>,
659    ) -> Result<PrunedNodeScan<'a>> {
660        let (scan, allocation) = GraphStore::scan_nodes_with_filter_pruning_admitted(
661            self, catalog, label_id, filter, admit,
662        )?;
663        Ok(PrunedNodeScan {
664            nodes: Box::new(scan.nodes.into_iter().map(move |node| {
665                let _retained_allocation = &allocation;
666                std::borrow::Cow::Borrowed(node)
667            })),
668            report: scan.report,
669        })
670    }
671}
672
673impl GraphExecutionWrite for GraphStore {
674    fn commit_mutation_with_limits(
675        &mut self,
676        catalog: &mut Catalog,
677        mutation: GraphMutation,
678        limits: MutationLimits,
679    ) -> Result<MutationSummary> {
680        GraphStore::commit_mutation_with_limits(self, catalog, mutation, limits)
681    }
682
683    fn commit_mutations_with_limits(
684        &mut self,
685        catalog: &mut Catalog,
686        mutations: Vec<GraphMutation>,
687        limits: MutationLimits,
688    ) -> Result<MutationSummary> {
689        GraphStore::commit_mutations_with_limits(self, catalog, mutations, limits)
690    }
691
692    fn set_node_properties_by_ids_with_limits(
693        &mut self,
694        catalog: &mut Catalog,
695        ids: &[NodeId],
696        assignments: &[NodeSetAssignment],
697        limits: MutationLimits,
698    ) -> Result<Vec<NodeId>> {
699        GraphStore::set_node_properties_by_ids_with_limits(self, catalog, ids, assignments, limits)
700    }
701
702    fn delete_node_ids_with_limits(
703        &mut self,
704        catalog: &mut Catalog,
705        ids: &[NodeId],
706        detach: bool,
707        limits: MutationLimits,
708    ) -> Result<Vec<NodeId>> {
709        GraphStore::delete_node_ids_with_limits(self, catalog, ids, detach, limits)
710    }
711}