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_adjacency_entries_with_allocation(
571        &self,
572        adjacency: (NodeId, Option<RelTypeId>, AdjacencyDirection),
573        memory: AdjacencyReadMemory<'_>,
574        admit: &mut hawdb_storage::read_view::ControlledGraphReadAllocator<'_>,
575        consumer: &mut dyn FnMut(OrderedAdjacencyEntry) -> Result<ScanControl>,
576    ) -> Result<ScanControl> {
577        GraphStore::visit_ordered_adjacency_entries_with_allocation(
578            self,
579            adjacency,
580            memory.budget_bytes,
581            admit,
582            |entry| consumer(entry).map(to_store_control),
583        )
584        .map(to_execution_control)
585    }
586
587    fn visit_filtered_ordered_relationships_with_allocation(
588        &self,
589        adjacency: (NodeId, Option<RelTypeId>, AdjacencyDirection),
590        filter: &PropertyFilter,
591        memory: AdjacencyReadMemory<'_>,
592        admit: &mut hawdb_storage::read_view::ControlledGraphReadAllocator<'_>,
593        consumer: &mut dyn FnMut(
594            hawdb_storage::read_view::AdmittedRelationshipRecord,
595        ) -> Result<ScanControl>,
596    ) -> Result<(ScanControl, Option<hawdb_storage::scan::ScanPruningReport>)> {
597        GraphStore::visit_filtered_ordered_relationships_with_allocation(
598            self,
599            adjacency,
600            filter,
601            memory.budget_bytes,
602            admit,
603            |row| consumer(row).map(to_store_control),
604        )
605        .map(|(control, report)| (to_execution_control(control), report))
606    }
607
608    fn visit_adjacent_relationships_with_filter_owned(
609        &self,
610        node_id: NodeId,
611        rel_type: Option<RelTypeId>,
612        direction: AdjacencyDirection,
613        filter: &PropertyFilter,
614        consumer: &mut dyn FnMut(RelRecord) -> Result<ScanControl>,
615    ) -> Result<(ScanControl, Option<hawdb_storage::scan::ScanPruningReport>)> {
616        let mut consumer_error = None;
617        let (control, report) = GraphStore::visit_adjacent_relationships_with_filter_owned(
618            self,
619            node_id,
620            rel_type,
621            direction,
622            filter,
623            |relationship| match consumer(relationship) {
624                Ok(control) => to_store_control(control),
625                Err(error) => {
626                    consumer_error = Some(error);
627                    GraphScanControl::Stop
628                }
629            },
630        )?;
631        match consumer_error {
632            Some(error) => Err(error),
633            None => Ok((to_execution_control(control), report)),
634        }
635    }
636
637    fn visit_ordered_adjacent_relationships_with_filter_owned(
638        &self,
639        node_id: NodeId,
640        rel_type: Option<RelTypeId>,
641        direction: AdjacencyDirection,
642        filter: &PropertyFilter,
643        memory: AdjacencyReadMemory<'_>,
644        consumer: &mut dyn FnMut(RelRecord) -> Result<ScanControl>,
645    ) -> Result<(ScanControl, Option<hawdb_storage::scan::ScanPruningReport>)> {
646        GraphStore::visit_filtered_ordered_relationships_with_allocation(
647            self,
648            (node_id, rel_type, direction),
649            filter,
650            memory.budget_bytes,
651            &mut hawdb_storage::read_view::ControlledGraphReadAllocator::new(
652                &mut |bytes| memory.admit_node(bytes, 0, None).map(Some),
653                None,
654            ),
655            |row| {
656                let (relationship, _allocation) = row.into_parts();
657                consumer(relationship).map(to_store_control)
658            },
659        )
660        .map(|(control, report)| (to_execution_control(control), report))
661    }
662
663    fn visit_relationships_owned(
664        &self,
665        rel_type: Option<RelTypeId>,
666        consumer: &mut dyn FnMut(RelRecord) -> Result<ScanControl>,
667    ) -> Result<ScanControl> {
668        GraphStore::try_visit_relationships_owned(self, rel_type, |relationship| {
669            consumer(relationship).map(to_store_control)
670        })
671        .map(to_execution_control)
672    }
673
674    fn scan_relationships_with_filter_pruning<'a>(
675        &'a self,
676        rel_type: Option<RelTypeId>,
677        filter: Option<&PropertyFilter>,
678    ) -> Result<PrunedRelationshipScan<'a>> {
679        let scan = GraphStore::scan_relationships_with_filter_pruning(self, rel_type, filter);
680        Ok(PrunedRelationshipScan {
681            relationships: Box::new(scan.relationships.into_iter().cloned()),
682            report: scan.report,
683        })
684    }
685
686    fn scan_nodes_with_filter_pruning<'a>(
687        &'a self,
688        catalog: &Catalog,
689        label_id: Option<LabelId>,
690        filter: Option<&PropertyFilter>,
691    ) -> Result<PrunedNodeScan<'a>> {
692        let scan = GraphStore::scan_nodes_with_filter_pruning(self, catalog, label_id, filter);
693        Ok(PrunedNodeScan {
694            nodes: Box::new(scan.nodes.into_iter().map(std::borrow::Cow::Borrowed)),
695            report: scan.report,
696        })
697    }
698
699    fn scan_nodes_with_filter_pruning_admitted<'a>(
700        &'a self,
701        catalog: &Catalog,
702        label_id: Option<LabelId>,
703        filter: Option<&PropertyFilter>,
704        admit: &mut dyn FnMut(
705            usize,
706        )
707            -> Result<Box<dyn hawdb_storage::read_view::GraphReadAllocation>>,
708    ) -> Result<PrunedNodeScan<'a>> {
709        let (scan, allocation) = GraphStore::scan_nodes_with_filter_pruning_admitted(
710            self, catalog, label_id, filter, admit,
711        )?;
712        Ok(PrunedNodeScan {
713            nodes: Box::new(scan.nodes.into_iter().map(move |node| {
714                let _retained_allocation = &allocation;
715                std::borrow::Cow::Borrowed(node)
716            })),
717            report: scan.report,
718        })
719    }
720}
721
722impl GraphExecutionWrite for GraphStore {
723    fn commit_mutation_with_limits(
724        &mut self,
725        catalog: &mut Catalog,
726        mutation: GraphMutation,
727        limits: MutationLimits,
728    ) -> Result<MutationSummary> {
729        GraphStore::commit_mutation_with_limits(self, catalog, mutation, limits)
730    }
731
732    fn commit_mutations_with_limits(
733        &mut self,
734        catalog: &mut Catalog,
735        mutations: Vec<GraphMutation>,
736        limits: MutationLimits,
737    ) -> Result<MutationSummary> {
738        GraphStore::commit_mutations_with_limits(self, catalog, mutations, limits)
739    }
740
741    fn set_node_properties_by_ids_with_limits(
742        &mut self,
743        catalog: &mut Catalog,
744        ids: &[NodeId],
745        assignments: &[NodeSetAssignment],
746        limits: MutationLimits,
747    ) -> Result<Vec<NodeId>> {
748        GraphStore::set_node_properties_by_ids_with_limits(self, catalog, ids, assignments, limits)
749    }
750
751    fn delete_node_ids_with_limits(
752        &mut self,
753        catalog: &mut Catalog,
754        ids: &[NodeId],
755        detach: bool,
756        limits: MutationLimits,
757    ) -> Result<Vec<NodeId>> {
758        GraphStore::delete_node_ids_with_limits(self, catalog, ids, detach, limits)
759    }
760}