Skip to main content

uqa_graph/
handle.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Graph runtime selection: memory is primary storage only for an in-memory
8//! engine; durable engines retain only session-bound storage handles.
9
10use crate::{
11    Direction, GraphLabelInfo, GraphLabelRegistry, GraphStore, GraphStoreResult, LabelKind,
12    MemoryGraphStore, PersistentGraphStore,
13};
14use std::collections::{BTreeMap, BTreeSet};
15use std::sync::Arc;
16use uqa_core::{Edge, Vertex};
17use uqa_storage::{CatalogFacade, PersistentStorageBackend};
18
19#[derive(Debug, Clone)]
20pub enum GraphStoreHandle {
21    Memory(MemoryGraphStore),
22    Persistent(PersistentGraphStore),
23}
24
25impl Default for GraphStoreHandle {
26    fn default() -> Self {
27        Self::Memory(MemoryGraphStore::new())
28    }
29}
30
31impl GraphStoreHandle {
32    pub fn from_catalog(
33        catalog: Arc<dyn CatalogFacade>,
34        backend: Arc<dyn PersistentStorageBackend>,
35    ) -> Self {
36        Self::Persistent(PersistentGraphStore::from_catalog(catalog, backend))
37    }
38
39    /// Preserve caller error types while storage checkpoints (or the primary
40    /// memory store) roll back the complete operation on errors and panics.
41    pub fn transaction_mapped<T, E>(
42        &mut self,
43        operation: impl FnOnce(&mut Self) -> Result<T, E>,
44        map_error: impl Fn(crate::GraphStoreError) -> E,
45    ) -> Result<T, E>
46    where
47        E: std::fmt::Display,
48    {
49        match self {
50            Self::Memory(store) => {
51                let backup = store.clone();
52                match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| operation(self))) {
53                    Ok(Ok(value)) => Ok(value),
54                    Ok(Err(error)) => {
55                        *self = Self::Memory(backup);
56                        Err(error)
57                    }
58                    Err(panic) => {
59                        *self = Self::Memory(backup);
60                        std::panic::resume_unwind(panic)
61                    }
62                }
63            }
64            Self::Persistent(store) => {
65                let mut checkpoint = store.clone();
66                checkpoint.transaction_mapped(|_| operation(self), map_error)
67            }
68        }
69    }
70
71    pub fn label_registry(&self, graph: &str) -> GraphStoreResult<GraphLabelRegistry> {
72        match self {
73            Self::Memory(store) => Ok(store.label_registry(graph)),
74            Self::Persistent(store) => store.label_registry(graph),
75        }
76    }
77    pub fn graph_labels(&self, graph: &str) -> GraphStoreResult<Vec<GraphLabelInfo>> {
78        match self {
79            Self::Memory(store) => store.graph_labels(graph),
80            Self::Persistent(store) => store.graph_labels(graph),
81        }
82    }
83    pub fn graph_label_kind(
84        &self,
85        graph: &str,
86        label: &str,
87    ) -> GraphStoreResult<Option<LabelKind>> {
88        match self {
89            Self::Memory(store) => store.graph_label_kind(graph, label),
90            Self::Persistent(store) => store.graph_label_kind(graph, label),
91        }
92    }
93    pub fn create_label(
94        &mut self,
95        graph: &str,
96        label: &str,
97        kind: LabelKind,
98    ) -> GraphStoreResult<Option<u32>> {
99        match self {
100            Self::Memory(store) => store.create_label(graph, label, kind),
101            Self::Persistent(store) => store.create_label(graph, label, kind),
102        }
103    }
104    pub fn drop_label(
105        &mut self,
106        graph: &str,
107        label: &str,
108    ) -> GraphStoreResult<Option<(u32, LabelKind)>> {
109        match self {
110            Self::Memory(store) => store.drop_label(graph, label),
111            Self::Persistent(store) => store.drop_label(graph, label),
112        }
113    }
114    pub fn rename_graph(&mut self, from: &str, to: &str) -> GraphStoreResult<()> {
115        match self {
116            Self::Memory(store) => store.rename_graph(from, to),
117            Self::Persistent(store) => store.rename_graph(from, to),
118        }
119    }
120    pub fn import_label_registry(
121        &mut self,
122        graph: &str,
123        registry: &GraphLabelRegistry,
124    ) -> GraphStoreResult<()> {
125        match self {
126            Self::Memory(store) => {
127                store.import_label_registry(graph, registry);
128                Ok(())
129            }
130            Self::Persistent(store) => store.import_label_registry(graph, registry),
131        }
132    }
133    pub fn rebuild_label_registry_from_ids(&mut self, graph: &str) -> GraphStoreResult<()> {
134        match self {
135            Self::Memory(store) => {
136                store.rebuild_label_registry_from_ids(graph);
137                Ok(())
138            }
139            Self::Persistent(store) => store.rebuild_label_registry_from_ids(graph),
140        }
141    }
142}
143
144impl GraphStore for GraphStoreHandle {
145    fn vertex_id_page(
146        &self,
147        graph: &str,
148        after: Option<u64>,
149        limit: usize,
150    ) -> GraphStoreResult<Vec<u64>> {
151        match self {
152            Self::Memory(store) => store.vertex_id_page(graph, after, limit),
153            Self::Persistent(store) => store.vertex_id_page(graph, after, limit),
154        }
155    }
156    fn edge_id_page(
157        &self,
158        graph: &str,
159        after: Option<u64>,
160        limit: usize,
161    ) -> GraphStoreResult<Vec<u64>> {
162        match self {
163            Self::Memory(store) => store.edge_id_page(graph, after, limit),
164            Self::Persistent(store) => store.edge_id_page(graph, after, limit),
165        }
166    }
167    fn transaction<T>(
168        &mut self,
169        operation: impl FnOnce(&mut Self) -> GraphStoreResult<T>,
170    ) -> GraphStoreResult<T> {
171        match self {
172            Self::Memory(store) => {
173                let backup = store.clone();
174                match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| operation(self))) {
175                    Ok(Ok(value)) => Ok(value),
176                    Ok(Err(error)) => {
177                        *self = Self::Memory(backup);
178                        Err(error)
179                    }
180                    Err(panic) => {
181                        *self = Self::Memory(backup);
182                        std::panic::resume_unwind(panic)
183                    }
184                }
185            }
186            Self::Persistent(store) => {
187                // This clone is a storage handle, never a graph snapshot.
188                let mut checkpoint = store.clone();
189                checkpoint.transaction(|_| operation(self))
190            }
191        }
192    }
193    fn create_graph(&mut self, name: &str) -> GraphStoreResult<()> {
194        match self {
195            Self::Memory(store) => GraphStore::create_graph(store, name),
196            Self::Persistent(store) => GraphStore::create_graph(store, name),
197        }
198    }
199    fn drop_graph(&mut self, name: &str) -> GraphStoreResult<()> {
200        match self {
201            Self::Memory(store) => GraphStore::drop_graph(store, name),
202            Self::Persistent(store) => GraphStore::drop_graph(store, name),
203        }
204    }
205    fn graph_names(&self) -> GraphStoreResult<Vec<String>> {
206        match self {
207            Self::Memory(store) => GraphStore::graph_names(store),
208            Self::Persistent(store) => GraphStore::graph_names(store),
209        }
210    }
211    fn has_graph(&self, name: &str) -> GraphStoreResult<bool> {
212        match self {
213            Self::Memory(store) => GraphStore::has_graph(store, name),
214            Self::Persistent(store) => GraphStore::has_graph(store, name),
215        }
216    }
217    fn union_graphs(&mut self, g1: &str, g2: &str, target: &str) -> GraphStoreResult<()> {
218        match self {
219            Self::Memory(store) => GraphStore::union_graphs(store, g1, g2, target),
220            Self::Persistent(store) => GraphStore::union_graphs(store, g1, g2, target),
221        }
222    }
223    fn intersect_graphs(&mut self, g1: &str, g2: &str, target: &str) -> GraphStoreResult<()> {
224        match self {
225            Self::Memory(store) => GraphStore::intersect_graphs(store, g1, g2, target),
226            Self::Persistent(store) => GraphStore::intersect_graphs(store, g1, g2, target),
227        }
228    }
229    fn difference_graphs(&mut self, g1: &str, g2: &str, target: &str) -> GraphStoreResult<()> {
230        match self {
231            Self::Memory(store) => GraphStore::difference_graphs(store, g1, g2, target),
232            Self::Persistent(store) => GraphStore::difference_graphs(store, g1, g2, target),
233        }
234    }
235    fn copy_graph(&mut self, source: &str, target: &str) -> GraphStoreResult<()> {
236        match self {
237            Self::Memory(store) => GraphStore::copy_graph(store, source, target),
238            Self::Persistent(store) => GraphStore::copy_graph(store, source, target),
239        }
240    }
241    fn add_vertex(&mut self, vertex: Vertex, graph: &str) -> GraphStoreResult<()> {
242        match self {
243            Self::Memory(store) => GraphStore::add_vertex(store, vertex, graph),
244            Self::Persistent(store) => GraphStore::add_vertex(store, vertex, graph),
245        }
246    }
247    fn add_edge(&mut self, edge: Edge, graph: &str) -> GraphStoreResult<()> {
248        match self {
249            Self::Memory(store) => GraphStore::add_edge(store, edge, graph),
250            Self::Persistent(store) => GraphStore::add_edge(store, edge, graph),
251        }
252    }
253    fn remove_vertex(&mut self, vertex_id: u64, graph: &str) -> GraphStoreResult<()> {
254        match self {
255            Self::Memory(store) => GraphStore::remove_vertex(store, vertex_id, graph),
256            Self::Persistent(store) => GraphStore::remove_vertex(store, vertex_id, graph),
257        }
258    }
259    fn remove_edge(&mut self, edge_id: u64, graph: &str) -> GraphStoreResult<()> {
260        match self {
261            Self::Memory(store) => GraphStore::remove_edge(store, edge_id, graph),
262            Self::Persistent(store) => GraphStore::remove_edge(store, edge_id, graph),
263        }
264    }
265    fn neighbors(
266        &self,
267        vertex_id: u64,
268        label: Option<&str>,
269        direction: Direction,
270        graph: &str,
271    ) -> GraphStoreResult<Vec<u64>> {
272        match self {
273            Self::Memory(store) => GraphStore::neighbors(store, vertex_id, label, direction, graph),
274            Self::Persistent(store) => {
275                GraphStore::neighbors(store, vertex_id, label, direction, graph)
276            }
277        }
278    }
279    fn vertices_by_label(&self, label: &str, graph: &str) -> GraphStoreResult<Vec<Vertex>> {
280        match self {
281            Self::Memory(store) => GraphStore::vertices_by_label(store, label, graph),
282            Self::Persistent(store) => GraphStore::vertices_by_label(store, label, graph),
283        }
284    }
285    fn vertex_ids_by_label(&self, label: &str, graph: &str) -> GraphStoreResult<Vec<u64>> {
286        match self {
287            Self::Memory(store) => GraphStore::vertex_ids_by_label(store, label, graph),
288            Self::Persistent(store) => GraphStore::vertex_ids_by_label(store, label, graph),
289        }
290    }
291    fn vertices_in_graph(&self, graph: &str) -> GraphStoreResult<Vec<Vertex>> {
292        match self {
293            Self::Memory(store) => GraphStore::vertices_in_graph(store, graph),
294            Self::Persistent(store) => GraphStore::vertices_in_graph(store, graph),
295        }
296    }
297    fn edges_in_graph(&self, graph: &str) -> GraphStoreResult<Vec<Edge>> {
298        match self {
299            Self::Memory(store) => GraphStore::edges_in_graph(store, graph),
300            Self::Persistent(store) => GraphStore::edges_in_graph(store, graph),
301        }
302    }
303    fn vertex_graphs(&self, vertex_id: u64) -> GraphStoreResult<BTreeSet<String>> {
304        match self {
305            Self::Memory(store) => GraphStore::vertex_graphs(store, vertex_id),
306            Self::Persistent(store) => GraphStore::vertex_graphs(store, vertex_id),
307        }
308    }
309    fn edge_graphs(&self, edge_id: u64) -> GraphStoreResult<BTreeSet<String>> {
310        match self {
311            Self::Memory(store) => store.edge_graphs(edge_id),
312            Self::Persistent(store) => store.edge_graphs(edge_id),
313        }
314    }
315    fn edges_by_label(&self, label: &str, graph: &str) -> GraphStoreResult<Vec<Edge>> {
316        match self {
317            Self::Memory(store) => store.edges_by_label(label, graph),
318            Self::Persistent(store) => store.edges_by_label(label, graph),
319        }
320    }
321    fn out_edge_ids(&self, vertex_id: u64, graph: &str) -> GraphStoreResult<BTreeSet<u64>> {
322        match self {
323            Self::Memory(store) => GraphStore::out_edge_ids(store, vertex_id, graph),
324            Self::Persistent(store) => GraphStore::out_edge_ids(store, vertex_id, graph),
325        }
326    }
327    fn in_edge_ids(&self, vertex_id: u64, graph: &str) -> GraphStoreResult<BTreeSet<u64>> {
328        match self {
329            Self::Memory(store) => GraphStore::in_edge_ids(store, vertex_id, graph),
330            Self::Persistent(store) => GraphStore::in_edge_ids(store, vertex_id, graph),
331        }
332    }
333    fn edge_ids_by_label(&self, label: &str, graph: &str) -> GraphStoreResult<BTreeSet<u64>> {
334        match self {
335            Self::Memory(store) => GraphStore::edge_ids_by_label(store, label, graph),
336            Self::Persistent(store) => GraphStore::edge_ids_by_label(store, label, graph),
337        }
338    }
339    fn vertex_ids_in_graph(&self, graph: &str) -> GraphStoreResult<BTreeSet<u64>> {
340        match self {
341            Self::Memory(store) => GraphStore::vertex_ids_in_graph(store, graph),
342            Self::Persistent(store) => GraphStore::vertex_ids_in_graph(store, graph),
343        }
344    }
345    fn require_vertex_in_graph(&self, vertex_id: u64, graph: &str) -> GraphStoreResult<()> {
346        match self {
347            Self::Memory(store) => GraphStore::require_vertex_in_graph(store, vertex_id, graph),
348            Self::Persistent(store) => GraphStore::require_vertex_in_graph(store, vertex_id, graph),
349        }
350    }
351    fn degree_distribution(&self, graph: &str) -> GraphStoreResult<BTreeMap<u64, u64>> {
352        match self {
353            Self::Memory(store) => GraphStore::degree_distribution(store, graph),
354            Self::Persistent(store) => GraphStore::degree_distribution(store, graph),
355        }
356    }
357    fn label_degree(&self, label: &str, graph: &str) -> GraphStoreResult<f64> {
358        match self {
359            Self::Memory(store) => GraphStore::label_degree(store, label, graph),
360            Self::Persistent(store) => GraphStore::label_degree(store, label, graph),
361        }
362    }
363    fn vertex_label_counts(&self, graph: &str) -> GraphStoreResult<BTreeMap<String, u64>> {
364        match self {
365            Self::Memory(store) => GraphStore::vertex_label_counts(store, graph),
366            Self::Persistent(store) => GraphStore::vertex_label_counts(store, graph),
367        }
368    }
369    fn get_vertex(&self, vertex_id: u64) -> GraphStoreResult<Option<Vertex>> {
370        match self {
371            Self::Memory(store) => GraphStore::get_vertex(store, vertex_id),
372            Self::Persistent(store) => GraphStore::get_vertex(store, vertex_id),
373        }
374    }
375    fn get_edge(&self, edge_id: u64) -> GraphStoreResult<Option<Edge>> {
376        match self {
377            Self::Memory(store) => GraphStore::get_edge(store, edge_id),
378            Self::Persistent(store) => GraphStore::get_edge(store, edge_id),
379        }
380    }
381    fn next_vertex_id(&mut self) -> GraphStoreResult<u64> {
382        match self {
383            Self::Memory(store) => GraphStore::next_vertex_id(store),
384            Self::Persistent(store) => GraphStore::next_vertex_id(store),
385        }
386    }
387    fn next_edge_id(&mut self) -> GraphStoreResult<u64> {
388        match self {
389            Self::Memory(store) => GraphStore::next_edge_id(store),
390            Self::Persistent(store) => GraphStore::next_edge_id(store),
391        }
392    }
393    fn allocate_vertex_id(&mut self, label: &str, graph: &str) -> GraphStoreResult<u64> {
394        match self {
395            Self::Memory(store) => GraphStore::allocate_vertex_id(store, label, graph),
396            Self::Persistent(store) => GraphStore::allocate_vertex_id(store, label, graph),
397        }
398    }
399    fn allocate_edge_id(&mut self, label: &str, graph: &str) -> GraphStoreResult<u64> {
400        match self {
401            Self::Memory(store) => GraphStore::allocate_edge_id(store, label, graph),
402            Self::Persistent(store) => GraphStore::allocate_edge_id(store, label, graph),
403        }
404    }
405    fn clear(&mut self) -> GraphStoreResult<()> {
406        match self {
407            Self::Memory(store) => GraphStore::clear(store),
408            Self::Persistent(store) => GraphStore::clear(store),
409        }
410    }
411    fn vertices(&self) -> GraphStoreResult<BTreeMap<u64, Vertex>> {
412        match self {
413            Self::Memory(store) => GraphStore::vertices(store),
414            Self::Persistent(store) => GraphStore::vertices(store),
415        }
416    }
417    fn edges(&self) -> GraphStoreResult<BTreeMap<u64, Edge>> {
418        match self {
419            Self::Memory(store) => GraphStore::edges(store),
420            Self::Persistent(store) => GraphStore::edges(store),
421        }
422    }
423}