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 hawdb_storage::read_view::GraphReadAllocator<'_>,
204        consumer: &mut dyn FnMut(ProjectedNodeRecord) -> Result<ScanControl>,
205    ) -> Result<ScanControl> {
206        GraphStore::visit_projected_nodes_admitted(self, label_id, properties, admit, &mut |node| {
207            consumer(node).map(to_store_control)
208        })
209        .map(to_execution_control)
210    }
211
212    fn visit_projected_nodes_with_allocation(
213        &self,
214        label_id: Option<LabelId>,
215        properties: &BTreeSet<String>,
216        admit: &mut hawdb_storage::read_view::ControlledGraphReadAllocator<'_>,
217        consumer: &mut dyn FnMut(
218            hawdb_storage::read_view::AdmittedProjectedNode,
219        ) -> Result<ScanControl>,
220    ) -> Result<ScanControl> {
221        GraphStore::visit_projected_nodes_with_allocation(
222            self,
223            label_id,
224            properties,
225            admit,
226            &mut |node| consumer(node).map(to_store_control),
227        )
228        .map(to_execution_control)
229    }
230
231    fn visit_projected_nodes_by_access_admitted(
232        &self,
233        label_id: LabelId,
234        access: &NodeProjectionAccess,
235        properties: &BTreeSet<String>,
236        admit: &mut hawdb_storage::read_view::GraphReadAllocator<'_>,
237        consumer: &mut dyn FnMut(ProjectedNodeRecord) -> Result<ScanControl>,
238    ) -> Result<ScanControl> {
239        GraphStore::visit_projected_nodes_by_access_admitted(
240            self,
241            label_id,
242            access,
243            properties,
244            admit,
245            &mut |node| consumer(node).map(to_store_control),
246        )
247        .map(to_execution_control)
248    }
249
250    fn visit_projected_nodes_by_property_admitted(
251        &self,
252        label_id: LabelId,
253        property: &str,
254        values: &[hawdb_core::Value],
255        properties: &BTreeSet<String>,
256        admit: &mut hawdb_storage::read_view::GraphReadAllocator<'_>,
257        consumer: &mut dyn FnMut(ProjectedNodeRecord) -> Result<ScanControl>,
258    ) -> Result<ScanControl> {
259        GraphStore::visit_projected_nodes_by_property_admitted(
260            self,
261            label_id,
262            property,
263            values,
264            properties,
265            admit,
266            &mut |node| consumer(node).map(to_store_control),
267        )
268        .map(to_execution_control)
269    }
270
271    fn visit_projected_nodes_owned(
272        &self,
273        label_id: Option<LabelId>,
274        required_properties: &BTreeSet<String>,
275        consumer: &mut dyn FnMut(ProjectedNodeRecord) -> Result<ScanControl>,
276    ) -> Result<ScanControl> {
277        let mut consumer_error = None;
278        let control =
279            GraphStore::visit_projected_nodes_owned(self, label_id, required_properties, |node| {
280                match consumer(node) {
281                    Ok(control) => to_store_control(control),
282                    Err(error) => {
283                        consumer_error = Some(error);
284                        GraphScanControl::Stop
285                    }
286                }
287            })?;
288        match consumer_error {
289            Some(error) => Err(error),
290            None => Ok(to_execution_control(control)),
291        }
292    }
293
294    fn visit_projected_nodes_by_access_owned(
295        &self,
296        label_id: LabelId,
297        access: &NodeProjectionAccess,
298        required_properties: &BTreeSet<String>,
299        consumer: &mut dyn FnMut(ProjectedNodeRecord) -> Result<ScanControl>,
300    ) -> Result<ScanControl> {
301        let mut consumer_error = None;
302        let control = GraphStore::visit_projected_nodes_by_access_owned(
303            self,
304            label_id,
305            access,
306            required_properties,
307            |node| match consumer(node) {
308                Ok(control) => to_store_control(control),
309                Err(error) => {
310                    consumer_error = Some(error);
311                    GraphScanControl::Stop
312                }
313            },
314        )?;
315        match consumer_error {
316            Some(error) => Err(error),
317            None => Ok(to_execution_control(control)),
318        }
319    }
320
321    fn visit_projected_nodes_by_property_owned(
322        &self,
323        label_id: LabelId,
324        property: &str,
325        values: &[hawdb_core::Value],
326        required_properties: &BTreeSet<String>,
327        consumer: &mut dyn FnMut(ProjectedNodeRecord) -> Result<ScanControl>,
328    ) -> Result<ScanControl> {
329        let mut consumer_error = None;
330        let control = GraphStore::visit_projected_nodes_by_property_owned(
331            self,
332            label_id,
333            property,
334            values,
335            required_properties,
336            |node| match consumer(node) {
337                Ok(control) => to_store_control(control),
338                Err(error) => {
339                    consumer_error = Some(error);
340                    GraphScanControl::Stop
341                }
342            },
343        )?;
344        match consumer_error {
345            Some(error) => Err(error),
346            None => Ok(to_execution_control(control)),
347        }
348    }
349
350    fn visit_nodes_by_property_owned(
351        &self,
352        label_id: LabelId,
353        property: &str,
354        values: &[hawdb_core::Value],
355        consumer: &mut dyn FnMut(NodeRecord) -> Result<ScanControl>,
356    ) -> Result<ScanControl> {
357        let mut consumer_error = None;
358        let control =
359            GraphStore::visit_nodes_by_property_owned(self, label_id, property, values, |node| {
360                match consumer(node) {
361                    Ok(control) => to_store_control(control),
362                    Err(error) => {
363                        consumer_error = Some(error);
364                        GraphScanControl::Stop
365                    }
366                }
367            })?;
368        match consumer_error {
369            Some(error) => Err(error),
370            None => Ok(to_execution_control(control)),
371        }
372    }
373
374    fn visit_nodes_by_composite_property_owned(
375        &self,
376        label_id: LabelId,
377        predicates: &[(String, hawdb_core::Value)],
378        consumer: &mut dyn FnMut(NodeRecord) -> Result<ScanControl>,
379    ) -> Result<ScanControl> {
380        adapt_node_consumer(consumer, |consumer| {
381            GraphStore::visit_nodes_by_composite_property_owned(
382                self, label_id, predicates, consumer,
383            )
384        })
385    }
386
387    fn visit_nodes_by_composite_range_owned(
388        &self,
389        label_id: LabelId,
390        seek: &hawdb_plan_cypher::CompositeRangeSeek,
391        consumer: &mut dyn FnMut(NodeRecord) -> Result<ScanControl>,
392    ) -> Result<ScanControl> {
393        adapt_node_consumer(consumer, |consumer| {
394            GraphStore::visit_nodes_by_composite_range_owned(self, label_id, seek, consumer)
395        })
396    }
397
398    fn visit_nodes_by_property_range_owned(
399        &self,
400        label_id: LabelId,
401        property: &str,
402        lower: Option<&(hawdb_core::Value, bool)>,
403        upper: Option<&(hawdb_core::Value, bool)>,
404        consumer: &mut dyn FnMut(NodeRecord) -> Result<ScanControl>,
405    ) -> Result<ScanControl> {
406        adapt_node_consumer(consumer, |consumer| {
407            GraphStore::visit_nodes_by_property_range_owned(
408                self, label_id, property, lower, upper, consumer,
409            )
410        })
411    }
412
413    fn visit_nodes_by_full_text_property_owned(
414        &self,
415        label_id: LabelId,
416        property: &str,
417        query: &str,
418        consumer: &mut dyn FnMut(NodeRecord) -> Result<ScanControl>,
419    ) -> Result<ScanControl> {
420        adapt_node_consumer(consumer, |consumer| {
421            GraphStore::visit_nodes_by_full_text_property_owned(
422                self, label_id, property, query, consumer,
423            )
424        })
425    }
426
427    fn projected_graph_definition(
428        &self,
429        name: &str,
430    ) -> Option<hawdb_storage::projection::ProjectedGraphDefinition> {
431        GraphStore::projected_graph_definition(self, name).cloned()
432    }
433
434    fn visit_source_scan_candidates(
435        &self,
436        predicate: &hawdb_storage::scan::ScanPredicate,
437        limits: SourceScanReadLimits,
438        task_context: Option<&hawdb_core::RuntimeTaskContext>,
439        consumer: &mut dyn FnMut(SourceScanCandidateRow) -> Result<ScanControl>,
440    ) -> Result<SourceScanCandidateVisit> {
441        let mut consumer_error = None;
442        let visit = GraphStore::visit_published_source_scan_candidates_bounded(
443            self,
444            predicate,
445            hawdb_storage::store::SourceScanCandidateLimits::bounded(
446                limits.io_depth,
447                limits.max_coalesced_bytes,
448                limits.max_wave_bytes,
449                limits.max_live_candidate_bytes,
450            ),
451            task_context,
452            &mut |row| match consumer(SourceScanCandidateRow {
453                node_id: row.node_id,
454                properties: row.properties,
455            }) {
456                Ok(control) => Ok(to_store_control(control)),
457                Err(error) => {
458                    consumer_error = Some(error);
459                    Ok(hawdb_storage::store::GraphScanControl::Stop)
460                }
461            },
462        )?;
463        if let Some(error) = consumer_error {
464            return Err(error);
465        }
466        Ok(match visit {
467            hawdb_storage::store::SourceScanCandidateVisit::Rows {
468                graph_epoch,
469                skipped_segment_count,
470                report,
471                candidate_count,
472            } => SourceScanCandidateVisit::Rows {
473                graph_epoch,
474                skipped_segment_count,
475                report,
476                candidate_count,
477            },
478            hawdb_storage::store::SourceScanCandidateVisit::Fallback(reason) => {
479                SourceScanCandidateVisit::Fallback(reason)
480            }
481        })
482    }
483
484    fn visit_adjacent_relationships_owned(
485        &self,
486        node_id: NodeId,
487        rel_type: Option<RelTypeId>,
488        direction: AdjacencyDirection,
489        consumer: &mut dyn FnMut(RelRecord) -> Result<ScanControl>,
490    ) -> Result<ScanControl> {
491        GraphStore::try_visit_adjacent_relationships_owned(
492            self,
493            node_id,
494            rel_type,
495            direction,
496            |relationship| consumer(relationship).map(to_store_control),
497        )
498        .map(to_execution_control)
499    }
500
501    fn visit_ordered_adjacent_relationships_owned(
502        &self,
503        node_id: NodeId,
504        rel_type: Option<RelTypeId>,
505        direction: AdjacencyDirection,
506        memory: AdjacencyReadMemory<'_>,
507        consumer: &mut dyn FnMut(RelRecord) -> Result<ScanControl>,
508    ) -> Result<ScanControl> {
509        GraphExecutionRead::visit_ordered_adjacent_relationships_with_allocation(
510            self,
511            node_id,
512            rel_type,
513            direction,
514            memory,
515            &mut hawdb_storage::read_view::ControlledGraphReadAllocator::new(
516                &mut |bytes| memory.admit_node(bytes, 0, None).map(Some),
517                None,
518            ),
519            &mut |relationship| {
520                let (relationship, _allocation) = relationship.into_parts();
521                consumer(relationship)
522            },
523        )
524    }
525
526    fn visit_ordered_adjacent_relationships_with_allocation(
527        &self,
528        node_id: NodeId,
529        rel_type: Option<RelTypeId>,
530        direction: AdjacencyDirection,
531        memory: AdjacencyReadMemory<'_>,
532        admit: &mut hawdb_storage::read_view::ControlledGraphReadAllocator<'_>,
533        consumer: &mut dyn FnMut(
534            hawdb_storage::read_view::AdmittedRelationshipRecord,
535        ) -> Result<ScanControl>,
536    ) -> Result<ScanControl> {
537        GraphStore::visit_ordered_adjacent_relationships_with_allocation(
538            self,
539            node_id,
540            rel_type,
541            direction,
542            memory.budget_bytes,
543            admit,
544            |relationship| consumer(relationship).map(to_store_control),
545        )
546        .map(to_execution_control)
547    }
548
549    fn visit_filtered_ordered_relationships_with_allocation(
550        &self,
551        adjacency: (NodeId, Option<RelTypeId>, AdjacencyDirection),
552        filter: &PropertyFilter,
553        memory: AdjacencyReadMemory<'_>,
554        admit: &mut hawdb_storage::read_view::ControlledGraphReadAllocator<'_>,
555        consumer: &mut dyn FnMut(
556            hawdb_storage::read_view::AdmittedRelationshipRecord,
557        ) -> Result<ScanControl>,
558    ) -> Result<(ScanControl, Option<hawdb_storage::scan::ScanPruningReport>)> {
559        GraphStore::visit_filtered_ordered_relationships_with_allocation(
560            self,
561            adjacency,
562            filter,
563            memory.budget_bytes,
564            admit,
565            |row| consumer(row).map(to_store_control),
566        )
567        .map(|(control, report)| (to_execution_control(control), report))
568    }
569
570    fn visit_adjacent_relationships_with_filter_owned(
571        &self,
572        node_id: NodeId,
573        rel_type: Option<RelTypeId>,
574        direction: AdjacencyDirection,
575        filter: &PropertyFilter,
576        consumer: &mut dyn FnMut(RelRecord) -> Result<ScanControl>,
577    ) -> Result<(ScanControl, Option<hawdb_storage::scan::ScanPruningReport>)> {
578        let mut consumer_error = None;
579        let (control, report) = GraphStore::visit_adjacent_relationships_with_filter_owned(
580            self,
581            node_id,
582            rel_type,
583            direction,
584            filter,
585            |relationship| match consumer(relationship) {
586                Ok(control) => to_store_control(control),
587                Err(error) => {
588                    consumer_error = Some(error);
589                    GraphScanControl::Stop
590                }
591            },
592        )?;
593        match consumer_error {
594            Some(error) => Err(error),
595            None => Ok((to_execution_control(control), report)),
596        }
597    }
598
599    fn visit_ordered_adjacent_relationships_with_filter_owned(
600        &self,
601        node_id: NodeId,
602        rel_type: Option<RelTypeId>,
603        direction: AdjacencyDirection,
604        filter: &PropertyFilter,
605        memory: AdjacencyReadMemory<'_>,
606        consumer: &mut dyn FnMut(RelRecord) -> Result<ScanControl>,
607    ) -> Result<(ScanControl, Option<hawdb_storage::scan::ScanPruningReport>)> {
608        GraphStore::visit_filtered_ordered_relationships_with_allocation(
609            self,
610            (node_id, rel_type, direction),
611            filter,
612            memory.budget_bytes,
613            &mut hawdb_storage::read_view::ControlledGraphReadAllocator::new(
614                &mut |bytes| memory.admit_node(bytes, 0, None).map(Some),
615                None,
616            ),
617            |row| {
618                let (relationship, _allocation) = row.into_parts();
619                consumer(relationship).map(to_store_control)
620            },
621        )
622        .map(|(control, report)| (to_execution_control(control), report))
623    }
624
625    fn visit_relationships_owned(
626        &self,
627        rel_type: Option<RelTypeId>,
628        consumer: &mut dyn FnMut(RelRecord) -> Result<ScanControl>,
629    ) -> Result<ScanControl> {
630        GraphStore::try_visit_relationships_owned(self, rel_type, |relationship| {
631            consumer(relationship).map(to_store_control)
632        })
633        .map(to_execution_control)
634    }
635
636    fn scan_relationships_with_filter_pruning<'a>(
637        &'a self,
638        rel_type: Option<RelTypeId>,
639        filter: Option<&PropertyFilter>,
640    ) -> Result<PrunedRelationshipScan<'a>> {
641        let scan = GraphStore::scan_relationships_with_filter_pruning(self, rel_type, filter);
642        Ok(PrunedRelationshipScan {
643            relationships: Box::new(scan.relationships.into_iter().cloned()),
644            report: scan.report,
645        })
646    }
647
648    fn scan_nodes_with_filter_pruning<'a>(
649        &'a self,
650        catalog: &Catalog,
651        label_id: Option<LabelId>,
652        filter: Option<&PropertyFilter>,
653    ) -> Result<PrunedNodeScan<'a>> {
654        let scan = GraphStore::scan_nodes_with_filter_pruning(self, catalog, label_id, filter);
655        Ok(PrunedNodeScan {
656            nodes: Box::new(scan.nodes.into_iter().map(std::borrow::Cow::Borrowed)),
657            report: scan.report,
658        })
659    }
660
661    fn scan_nodes_with_filter_pruning_admitted<'a>(
662        &'a self,
663        catalog: &Catalog,
664        label_id: Option<LabelId>,
665        filter: Option<&PropertyFilter>,
666        admit: &mut dyn FnMut(
667            usize,
668        )
669            -> Result<Box<dyn hawdb_storage::read_view::GraphReadAllocation>>,
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}