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