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