Skip to main content

uqa_graph/persistent_store/
storage.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Physical graph access shared by engine catalogs and standalone `SQLite`.
8
9use uqa_core::{Edge, Vertex};
10use uqa_storage::{GraphEntityFilter, GraphEntityKind};
11
12use crate::{GraphLabelRegistry, GraphStoreResult};
13
14/// Open a write transaction or a nested savepoint on the exact storage session.
15pub fn begin_graph_write(
16    backend: std::sync::Arc<dyn uqa_storage::PersistentStorageBackend>,
17) -> GraphStoreResult<Box<dyn GraphWriteTransaction>> {
18    let savepoint = if backend.in_transaction() {
19        let id = uqa_storage::StorageSavepointId::allocate();
20        backend.savepoint(id)?;
21        Some(id)
22    } else {
23        backend.begin_transaction()?;
24        None
25    };
26    Ok(Box::new(StorageGraphWriteTransaction {
27        backend,
28        savepoint,
29        active: true,
30    }))
31}
32
33struct StorageGraphWriteTransaction {
34    backend: std::sync::Arc<dyn uqa_storage::PersistentStorageBackend>,
35    savepoint: Option<uqa_storage::StorageSavepointId>,
36    active: bool,
37}
38
39impl GraphWriteTransaction for StorageGraphWriteTransaction {
40    fn commit(&mut self) -> GraphStoreResult<()> {
41        if let Some(id) = self.savepoint {
42            self.backend.release_savepoint(id)?;
43        } else {
44            self.backend.commit_transaction()?;
45        }
46        self.active = false;
47        Ok(())
48    }
49
50    fn rollback(&mut self) -> GraphStoreResult<()> {
51        if !self.active {
52            return Ok(());
53        }
54        if let Some(id) = self.savepoint {
55            self.backend.rollback_to_savepoint(id)?;
56            self.backend.release_savepoint(id)?;
57        } else {
58            self.backend.rollback_transaction()?;
59        }
60        self.active = false;
61        Ok(())
62    }
63}
64
65impl Drop for StorageGraphWriteTransaction {
66    fn drop(&mut self) {
67        if self.active {
68            let _ = self.rollback();
69        }
70    }
71}
72
73/// A live storage checkpoint. Implementations also roll back on Drop so a
74/// panic cannot publish half a graph mutation.
75pub trait GraphWriteTransaction {
76    fn commit(&mut self) -> GraphStoreResult<()>;
77    fn rollback(&mut self) -> GraphStoreResult<()>;
78}
79
80/// No entity, membership, or adjacency collection is retained by a handle.
81/// Multi-read operations run in the caller's pinned storage transaction.
82pub trait GraphStorage: Send + Sync {
83    /// Copy only transaction-local write identities when preparing a new
84    /// command candidate. The underlying durable snapshots remain shared.
85    fn fork_overlay(&self) -> Option<std::sync::Arc<dyn GraphStorage>> {
86        None
87    }
88    fn unmodified_read_snapshot(&self) -> Option<super::PersistentGraphStore> {
89        None
90    }
91    fn begin_write(&self) -> GraphStoreResult<Box<dyn GraphWriteTransaction>>;
92    fn graph_names(&self) -> GraphStoreResult<Vec<String>>;
93    fn has_graph(&self, graph: &str) -> GraphStoreResult<bool>;
94    fn create_graph(&self, graph: &str) -> GraphStoreResult<()>;
95    fn delete_graph(&self, graph: &str) -> GraphStoreResult<()>;
96    fn registry(&self, graph: &str) -> GraphStoreResult<GraphLabelRegistry>;
97    fn save_registry(&self, graph: &str, registry: &GraphLabelRegistry) -> GraphStoreResult<()>;
98    fn counter(&self, kind: GraphEntityKind) -> GraphStoreResult<Option<u64>>;
99    fn save_counter(&self, kind: GraphEntityKind, next: u64) -> GraphStoreResult<()>;
100    fn vertex(&self, id: u64) -> GraphStoreResult<Option<Vertex>>;
101    fn edge(&self, id: u64) -> GraphStoreResult<Option<Edge>>;
102    fn save_vertex(&self, vertex: &Vertex) -> GraphStoreResult<()>;
103    fn save_edge(&self, edge: &Edge) -> GraphStoreResult<()>;
104    fn delete_vertex(&self, id: u64) -> GraphStoreResult<()>;
105    fn delete_edge(&self, id: u64) -> GraphStoreResult<()>;
106    fn ids(
107        &self,
108        filter: GraphEntityFilter<'_>,
109        after: Option<u64>,
110        limit: usize,
111    ) -> GraphStoreResult<Vec<u64>>;
112    fn count(&self, filter: GraphEntityFilter<'_>) -> GraphStoreResult<u64>;
113    fn max_id(&self, kind: GraphEntityKind) -> GraphStoreResult<Option<u64>>;
114    fn memberships(&self, kind: GraphEntityKind, id: u64) -> GraphStoreResult<Vec<String>>;
115    fn has_membership(&self, kind: GraphEntityKind, id: u64, graph: &str)
116        -> GraphStoreResult<bool>;
117    fn attach(&self, kind: GraphEntityKind, id: u64, graph: &str) -> GraphStoreResult<()>;
118    fn detach(&self, kind: GraphEntityKind, id: u64, graph: &str) -> GraphStoreResult<()>;
119}