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 scan_indexed_nodes_borrowed<'a>(
123        &'a self,
124        label_id: LabelId,
125        property: &str,
126        value: &hawdb_core::Value,
127    ) -> Option<Box<dyn Iterator<Item = &'a NodeRecord> + 'a>> {
128        Some(Box::new(GraphStore::seek_nodes_by_property(
129            self, label_id, property, value,
130        )))
131    }
132
133    fn node_count_for_label(&self, label_id: Option<LabelId>) -> usize {
134        GraphStore::node_count_for_label(self, label_id)
135    }
136
137    fn relationship_count_for_type(&self, rel_type: Option<RelTypeId>) -> usize {
138        GraphStore::relationship_count_for_type(self, rel_type)
139    }
140
141    fn visit_nodes_owned(
142        &self,
143        label_id: Option<LabelId>,
144        consumer: &mut dyn FnMut(NodeRecord) -> Result<ScanControl>,
145    ) -> Result<ScanControl> {
146        GraphStore::try_visit_nodes_owned(self, label_id, |node| {
147            consumer(node).map(to_store_control)
148        })
149        .map(to_execution_control)
150    }
151
152    fn visit_relationships_with_allocation(
153        &self,
154        rel_type: Option<RelTypeId>,
155        admit: &mut hawdb_storage::read_view::ControlledGraphReadAllocator<'_>,
156        consumer: &mut dyn FnMut(
157            hawdb_storage::read_view::AdmittedRelationshipRecord,
158        ) -> Result<ScanControl>,
159    ) -> Result<ScanControl> {
160        GraphStore::visit_relationships_with_allocation(self, rel_type, admit, &mut |record| {
161            consumer(record).map(to_store_control)
162        })
163        .map(to_execution_control)
164    }
165
166    fn visit_nodes_with_allocation(
167        &self,
168        label_id: Option<LabelId>,
169        admit: &mut hawdb_storage::read_view::ControlledGraphReadAllocator<'_>,
170        consumer: &mut dyn FnMut(
171            hawdb_storage::read_view::AdmittedNodeRecord,
172        ) -> Result<ScanControl>,
173    ) -> Result<ScanControl> {
174        GraphStore::visit_nodes_with_allocation(self, label_id, admit, &mut |node| {
175            consumer(node).map(to_store_control)
176        })
177        .map(to_execution_control)
178    }
179
180    fn visit_nodes_by_access_with_allocation(
181        &self,
182        label_id: LabelId,
183        access: &NodeProjectionAccess,
184        admit: &mut hawdb_storage::read_view::ControlledGraphReadAllocator<'_>,
185        consumer: &mut dyn FnMut(
186            hawdb_storage::read_view::AdmittedNodeRecord,
187        ) -> Result<ScanControl>,
188    ) -> Result<ScanControl> {
189        GraphStore::visit_nodes_by_access_with_allocation(
190            self,
191            label_id,
192            access,
193            admit,
194            &mut |node| consumer(node).map(to_store_control),
195        )
196        .map(to_execution_control)
197    }
198
199    fn visit_projected_nodes_admitted(
200        &self,
201        label_id: Option<LabelId>,
202        properties: &BTreeSet<String>,
203        admit: &mut dyn FnMut(
204            usize,
205        )
206            -> Result<Box<dyn hawdb_storage::read_view::GraphReadAllocation>>,
207        consumer: &mut dyn FnMut(ProjectedNodeRecord) -> Result<ScanControl>,
208    ) -> Result<ScanControl> {
209        GraphStore::visit_projected_nodes_admitted(self, label_id, properties, admit, &mut |node| {
210            consumer(node).map(to_store_control)
211        })
212        .map(to_execution_control)
213    }
214
215    fn visit_projected_nodes_with_allocation(
216        &self,
217        label_id: Option<LabelId>,
218        properties: &BTreeSet<String>,
219        admit: &mut hawdb_storage::read_view::ControlledGraphReadAllocator<'_>,
220        consumer: &mut dyn FnMut(
221            hawdb_storage::read_view::AdmittedProjectedNode,
222        ) -> Result<ScanControl>,
223    ) -> Result<ScanControl> {
224        GraphStore::visit_projected_nodes_with_allocation(
225            self,
226            label_id,
227            properties,
228            admit,
229            &mut |node| consumer(node).map(to_store_control),
230        )
231        .map(to_execution_control)
232    }
233
234    fn visit_projected_nodes_by_access_admitted(
235        &self,
236        label_id: LabelId,
237        access: &NodeProjectionAccess,
238        properties: &BTreeSet<String>,
239        admit: &mut dyn FnMut(
240            usize,
241        )
242            -> Result<Box<dyn hawdb_storage::read_view::GraphReadAllocation>>,
243        consumer: &mut dyn FnMut(ProjectedNodeRecord) -> Result<ScanControl>,
244    ) -> Result<ScanControl> {
245        GraphStore::visit_projected_nodes_by_access_admitted(
246            self,
247            label_id,
248            access,
249            properties,
250            admit,
251            &mut |node| consumer(node).map(to_store_control),
252        )
253        .map(to_execution_control)
254    }
255
256    fn visit_projected_nodes_by_property_admitted(
257        &self,
258        label_id: LabelId,
259        property: &str,
260        values: &[hawdb_core::Value],
261        properties: &BTreeSet<String>,
262        admit: &mut dyn FnMut(
263            usize,
264        )
265            -> Result<Box<dyn hawdb_storage::read_view::GraphReadAllocation>>,
266        consumer: &mut dyn FnMut(ProjectedNodeRecord) -> Result<ScanControl>,
267    ) -> Result<ScanControl> {
268        GraphStore::visit_projected_nodes_by_property_admitted(
269            self,
270            label_id,
271            property,
272            values,
273            properties,
274            admit,
275            &mut |node| consumer(node).map(to_store_control),
276        )
277        .map(to_execution_control)
278    }
279
280    fn visit_projected_nodes_owned(
281        &self,
282        label_id: Option<LabelId>,
283        required_properties: &BTreeSet<String>,
284        consumer: &mut dyn FnMut(ProjectedNodeRecord) -> Result<ScanControl>,
285    ) -> Result<ScanControl> {
286        let mut consumer_error = None;
287        let control =
288            GraphStore::visit_projected_nodes_owned(self, label_id, required_properties, |node| {
289                match consumer(node) {
290                    Ok(control) => to_store_control(control),
291                    Err(error) => {
292                        consumer_error = Some(error);
293                        GraphScanControl::Stop
294                    }
295                }
296            })?;
297        match consumer_error {
298            Some(error) => Err(error),
299            None => Ok(to_execution_control(control)),
300        }
301    }
302
303    fn visit_projected_nodes_by_access_owned(
304        &self,
305        label_id: LabelId,
306        access: &NodeProjectionAccess,
307        required_properties: &BTreeSet<String>,
308        consumer: &mut dyn FnMut(ProjectedNodeRecord) -> Result<ScanControl>,
309    ) -> Result<ScanControl> {
310        let mut consumer_error = None;
311        let control = GraphStore::visit_projected_nodes_by_access_owned(
312            self,
313            label_id,
314            access,
315            required_properties,
316            |node| match consumer(node) {
317                Ok(control) => to_store_control(control),
318                Err(error) => {
319                    consumer_error = Some(error);
320                    GraphScanControl::Stop
321                }
322            },
323        )?;
324        match consumer_error {
325            Some(error) => Err(error),
326            None => Ok(to_execution_control(control)),
327        }
328    }
329
330    fn visit_projected_nodes_by_property_owned(
331        &self,
332        label_id: LabelId,
333        property: &str,
334        values: &[hawdb_core::Value],
335        required_properties: &BTreeSet<String>,
336        consumer: &mut dyn FnMut(ProjectedNodeRecord) -> Result<ScanControl>,
337    ) -> Result<ScanControl> {
338        let mut consumer_error = None;
339        let control = GraphStore::visit_projected_nodes_by_property_owned(
340            self,
341            label_id,
342            property,
343            values,
344            required_properties,
345            |node| match consumer(node) {
346                Ok(control) => to_store_control(control),
347                Err(error) => {
348                    consumer_error = Some(error);
349                    GraphScanControl::Stop
350                }
351            },
352        )?;
353        match consumer_error {
354            Some(error) => Err(error),
355            None => Ok(to_execution_control(control)),
356        }
357    }
358
359    fn visit_nodes_by_property_owned(
360        &self,
361        label_id: LabelId,
362        property: &str,
363        values: &[hawdb_core::Value],
364        consumer: &mut dyn FnMut(NodeRecord) -> Result<ScanControl>,
365    ) -> Result<ScanControl> {
366        let mut consumer_error = None;
367        let control =
368            GraphStore::visit_nodes_by_property_owned(self, label_id, property, values, |node| {
369                match consumer(node) {
370                    Ok(control) => to_store_control(control),
371                    Err(error) => {
372                        consumer_error = Some(error);
373                        GraphScanControl::Stop
374                    }
375                }
376            })?;
377        match consumer_error {
378            Some(error) => Err(error),
379            None => Ok(to_execution_control(control)),
380        }
381    }
382
383    fn visit_nodes_by_composite_property_owned(
384        &self,
385        label_id: LabelId,
386        predicates: &[(String, hawdb_core::Value)],
387        consumer: &mut dyn FnMut(NodeRecord) -> Result<ScanControl>,
388    ) -> Result<ScanControl> {
389        adapt_node_consumer(consumer, |consumer| {
390            GraphStore::visit_nodes_by_composite_property_owned(
391                self, label_id, predicates, consumer,
392            )
393        })
394    }
395
396    fn visit_nodes_by_composite_range_owned(
397        &self,
398        label_id: LabelId,
399        seek: &hawdb_plan_cypher::CompositeRangeSeek,
400        consumer: &mut dyn FnMut(NodeRecord) -> Result<ScanControl>,
401    ) -> Result<ScanControl> {
402        adapt_node_consumer(consumer, |consumer| {
403            GraphStore::visit_nodes_by_composite_range_owned(self, label_id, seek, consumer)
404        })
405    }
406
407    fn visit_nodes_by_property_range_owned(
408        &self,
409        label_id: LabelId,
410        property: &str,
411        lower: Option<&(hawdb_core::Value, bool)>,
412        upper: Option<&(hawdb_core::Value, bool)>,
413        consumer: &mut dyn FnMut(NodeRecord) -> Result<ScanControl>,
414    ) -> Result<ScanControl> {
415        adapt_node_consumer(consumer, |consumer| {
416            GraphStore::visit_nodes_by_property_range_owned(
417                self, label_id, property, lower, upper, consumer,
418            )
419        })
420    }
421
422    fn visit_nodes_by_full_text_property_owned(
423        &self,
424        label_id: LabelId,
425        property: &str,
426        query: &str,
427        consumer: &mut dyn FnMut(NodeRecord) -> Result<ScanControl>,
428    ) -> Result<ScanControl> {
429        adapt_node_consumer(consumer, |consumer| {
430            GraphStore::visit_nodes_by_full_text_property_owned(
431                self, label_id, property, query, consumer,
432            )
433        })
434    }
435
436    fn projected_graph_definition(
437        &self,
438        name: &str,
439    ) -> Option<hawdb_storage::projection::ProjectedGraphDefinition> {
440        GraphStore::projected_graph_definition(self, name).cloned()
441    }
442
443    fn visit_source_scan_candidates(
444        &self,
445        predicate: &hawdb_storage::scan::ScanPredicate,
446        limits: SourceScanReadLimits,
447        task_context: Option<&hawdb_core::RuntimeTaskContext>,
448        consumer: &mut dyn FnMut(SourceScanCandidateRow) -> Result<ScanControl>,
449    ) -> Result<SourceScanCandidateVisit> {
450        let mut consumer_error = None;
451        let visit = GraphStore::visit_published_source_scan_candidates_bounded(
452            self,
453            predicate,
454            hawdb_storage::store::SourceScanCandidateLimits::bounded(
455                limits.io_depth,
456                limits.max_coalesced_bytes,
457                limits.max_wave_bytes,
458                limits.max_live_candidate_bytes,
459            ),
460            task_context,
461            &mut |row| match consumer(SourceScanCandidateRow {
462                node_id: row.node_id,
463                properties: row.properties,
464            }) {
465                Ok(control) => Ok(to_store_control(control)),
466                Err(error) => {
467                    consumer_error = Some(error);
468                    Ok(hawdb_storage::store::GraphScanControl::Stop)
469                }
470            },
471        )?;
472        if let Some(error) = consumer_error {
473            return Err(error);
474        }
475        Ok(match visit {
476            hawdb_storage::store::SourceScanCandidateVisit::Rows {
477                graph_epoch,
478                skipped_segment_count,
479                report,
480                candidate_count,
481            } => SourceScanCandidateVisit::Rows {
482                graph_epoch,
483                skipped_segment_count,
484                report,
485                candidate_count,
486            },
487            hawdb_storage::store::SourceScanCandidateVisit::Fallback(reason) => {
488                SourceScanCandidateVisit::Fallback(reason)
489            }
490        })
491    }
492
493    fn visit_adjacent_relationships_owned(
494        &self,
495        node_id: NodeId,
496        rel_type: Option<RelTypeId>,
497        direction: AdjacencyDirection,
498        consumer: &mut dyn FnMut(RelRecord) -> Result<ScanControl>,
499    ) -> Result<ScanControl> {
500        GraphStore::try_visit_adjacent_relationships_owned(
501            self,
502            node_id,
503            rel_type,
504            direction,
505            |relationship| consumer(relationship).map(to_store_control),
506        )
507        .map(to_execution_control)
508    }
509
510    fn visit_ordered_adjacent_relationships_owned(
511        &self,
512        node_id: NodeId,
513        rel_type: Option<RelTypeId>,
514        direction: AdjacencyDirection,
515        memory: AdjacencyReadMemory<'_>,
516        consumer: &mut dyn FnMut(RelRecord) -> Result<ScanControl>,
517    ) -> Result<ScanControl> {
518        GraphExecutionRead::visit_ordered_adjacent_relationships_with_allocation(
519            self,
520            node_id,
521            rel_type,
522            direction,
523            memory,
524            &mut |bytes| memory.admit_node(bytes, 0, None).map(Some),
525            &mut |relationship| {
526                let (relationship, _allocation) = relationship.into_parts();
527                consumer(relationship)
528            },
529        )
530    }
531
532    fn visit_ordered_adjacent_relationships_with_allocation(
533        &self,
534        node_id: NodeId,
535        rel_type: Option<RelTypeId>,
536        direction: AdjacencyDirection,
537        memory: AdjacencyReadMemory<'_>,
538        admit: &mut hawdb_storage::read_view::ControlledGraphReadAllocator<'_>,
539        consumer: &mut dyn FnMut(
540            hawdb_storage::read_view::AdmittedRelationshipRecord,
541        ) -> Result<ScanControl>,
542    ) -> Result<ScanControl> {
543        GraphStore::visit_ordered_adjacent_relationships_with_allocation(
544            self,
545            node_id,
546            rel_type,
547            direction,
548            memory.budget_bytes,
549            admit,
550            |relationship| consumer(relationship).map(to_store_control),
551        )
552        .map(to_execution_control)
553    }
554
555    fn visit_filtered_ordered_relationships_with_allocation(
556        &self,
557        adjacency: (NodeId, Option<RelTypeId>, AdjacencyDirection),
558        filter: &PropertyFilter,
559        memory: AdjacencyReadMemory<'_>,
560        admit: &mut hawdb_storage::read_view::ControlledGraphReadAllocator<'_>,
561        consumer: &mut dyn FnMut(
562            hawdb_storage::read_view::AdmittedRelationshipRecord,
563        ) -> Result<ScanControl>,
564    ) -> Result<(ScanControl, Option<hawdb_storage::scan::ScanPruningReport>)> {
565        GraphStore::visit_filtered_ordered_relationships_with_allocation(
566            self,
567            adjacency,
568            filter,
569            memory.budget_bytes,
570            admit,
571            |row| consumer(row).map(to_store_control),
572        )
573        .map(|(control, report)| (to_execution_control(control), report))
574    }
575
576    fn visit_adjacent_relationships_with_filter_owned(
577        &self,
578        node_id: NodeId,
579        rel_type: Option<RelTypeId>,
580        direction: AdjacencyDirection,
581        filter: &PropertyFilter,
582        consumer: &mut dyn FnMut(RelRecord) -> Result<ScanControl>,
583    ) -> Result<(ScanControl, Option<hawdb_storage::scan::ScanPruningReport>)> {
584        let mut consumer_error = None;
585        let (control, report) = GraphStore::visit_adjacent_relationships_with_filter_owned(
586            self,
587            node_id,
588            rel_type,
589            direction,
590            filter,
591            |relationship| match consumer(relationship) {
592                Ok(control) => to_store_control(control),
593                Err(error) => {
594                    consumer_error = Some(error);
595                    GraphScanControl::Stop
596                }
597            },
598        )?;
599        match consumer_error {
600            Some(error) => Err(error),
601            None => Ok((to_execution_control(control), report)),
602        }
603    }
604
605    fn visit_ordered_adjacent_relationships_with_filter_owned(
606        &self,
607        node_id: NodeId,
608        rel_type: Option<RelTypeId>,
609        direction: AdjacencyDirection,
610        filter: &PropertyFilter,
611        memory: AdjacencyReadMemory<'_>,
612        consumer: &mut dyn FnMut(RelRecord) -> Result<ScanControl>,
613    ) -> Result<(ScanControl, Option<hawdb_storage::scan::ScanPruningReport>)> {
614        GraphStore::visit_filtered_ordered_relationships_with_allocation(
615            self,
616            (node_id, rel_type, direction),
617            filter,
618            memory.budget_bytes,
619            &mut |bytes| memory.admit_node(bytes, 0, None).map(Some),
620            |row| {
621                let (relationship, _allocation) = row.into_parts();
622                consumer(relationship).map(to_store_control)
623            },
624        )
625        .map(|(control, report)| (to_execution_control(control), report))
626    }
627
628    fn visit_relationships_owned(
629        &self,
630        rel_type: Option<RelTypeId>,
631        consumer: &mut dyn FnMut(RelRecord) -> Result<ScanControl>,
632    ) -> Result<ScanControl> {
633        GraphStore::try_visit_relationships_owned(self, rel_type, |relationship| {
634            consumer(relationship).map(to_store_control)
635        })
636        .map(to_execution_control)
637    }
638
639    fn scan_relationships_with_filter_pruning<'a>(
640        &'a self,
641        rel_type: Option<RelTypeId>,
642        filter: Option<&PropertyFilter>,
643    ) -> Result<PrunedRelationshipScan<'a>> {
644        let scan = GraphStore::scan_relationships_with_filter_pruning(self, rel_type, filter);
645        Ok(PrunedRelationshipScan {
646            relationships: Box::new(scan.relationships.into_iter().cloned()),
647            report: scan.report,
648        })
649    }
650
651    fn scan_nodes_with_filter_pruning<'a>(
652        &'a self,
653        catalog: &Catalog,
654        label_id: Option<LabelId>,
655        filter: Option<&PropertyFilter>,
656    ) -> Result<PrunedNodeScan<'a>> {
657        let scan = GraphStore::scan_nodes_with_filter_pruning(self, catalog, label_id, filter);
658        Ok(PrunedNodeScan {
659            nodes: Box::new(scan.nodes.into_iter().map(std::borrow::Cow::Borrowed)),
660            report: scan.report,
661        })
662    }
663
664    fn scan_nodes_with_filter_pruning_admitted<'a>(
665        &'a self,
666        catalog: &Catalog,
667        label_id: Option<LabelId>,
668        filter: Option<&PropertyFilter>,
669        admit: &mut hawdb_storage::read_view::GraphReadAllocator<'_>,
670    ) -> Result<PrunedNodeScan<'a>> {
671        let (scan, allocation) = GraphStore::scan_nodes_with_filter_pruning_admitted(
672            self, catalog, label_id, filter, admit,
673        )?;
674        Ok(PrunedNodeScan {
675            nodes: Box::new(scan.nodes.into_iter().map(move |node| {
676                let _retained_allocation = &allocation;
677                std::borrow::Cow::Borrowed(node)
678            })),
679            report: scan.report,
680        })
681    }
682}
683
684impl GraphExecutionWrite for GraphStore {
685    fn commit_mutation_with_limits(
686        &mut self,
687        catalog: &mut Catalog,
688        mutation: GraphMutation,
689        limits: MutationLimits,
690    ) -> Result<MutationSummary> {
691        GraphStore::commit_mutation_with_limits(self, catalog, mutation, limits)
692    }
693
694    fn commit_mutations_with_limits(
695        &mut self,
696        catalog: &mut Catalog,
697        mutations: Vec<GraphMutation>,
698        limits: MutationLimits,
699    ) -> Result<MutationSummary> {
700        GraphStore::commit_mutations_with_limits(self, catalog, mutations, limits)
701    }
702
703    fn set_node_properties_by_ids_with_limits(
704        &mut self,
705        catalog: &mut Catalog,
706        ids: &[NodeId],
707        assignments: &[NodeSetAssignment],
708        limits: MutationLimits,
709    ) -> Result<Vec<NodeId>> {
710        GraphStore::set_node_properties_by_ids_with_limits(self, catalog, ids, assignments, limits)
711    }
712
713    fn delete_node_ids_with_limits(
714        &mut self,
715        catalog: &mut Catalog,
716        ids: &[NodeId],
717        detach: bool,
718        limits: MutationLimits,
719    ) -> Result<Vec<NodeId>> {
720        GraphStore::delete_node_ids_with_limits(self, catalog, ids, detach, limits)
721    }
722}