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