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