1use core::fmt;
4
5use oxgraph_csc::CscSnapshotError;
6use oxgraph_csr::CsrSnapshotError;
7use oxgraph_snapshot::{PlanError, SnapshotError};
8
9use crate::{catalog::CatalogError, role::GraphRole};
10
11#[derive(Debug)]
13pub enum BuildError {
14 Plan(PlanError),
16 Graph(oxgraph_csr::build::GraphBuildError<u32, u32>),
18 MissingSnapshotBytes,
20 EmptyEdges,
22 NodeCountOverflow,
24 EdgeCountOverflow,
26 EdgeCountMismatch,
28 MissingNodeKey,
30 MissingCsrSection {
32 kind: u32,
34 },
35 MissingMetadataSection,
37 MalformedMetadata(alloc::string::String),
39 MissingReverseIndex,
41 TopologyNodeCountMismatch,
43 TopologyEdgeCountMismatch,
45 MetadataNodeCountMismatch,
47 MetadataEdgeCountMismatch,
49 Spi(alloc::string::String),
51}
52
53impl fmt::Display for BuildError {
54 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55 match self {
56 Self::Plan(error) => write!(f, "plan error: {error}"),
57 Self::Graph(error) => write!(f, "graph build error: {error}"),
58 Self::MissingSnapshotBytes => f.write_str("snapshot bytes required"),
59 Self::EmptyEdges => f.write_str("build requires at least one edge row"),
60 Self::NodeCountOverflow => f.write_str("node count does not fit in u32 metadata"),
61 Self::EdgeCountOverflow => f.write_str("edge count does not fit in u32 metadata"),
62 Self::EdgeCountMismatch | Self::TopologyEdgeCountMismatch => {
63 f.write_str("forward and inbound edge counts diverged")
64 }
65 Self::MissingNodeKey => f.write_str("edge row references missing node key"),
66 Self::MissingCsrSection { kind } => {
67 write!(f, "missing CSR section {kind:#06x}")
68 }
69 Self::MissingMetadataSection => f.write_str("postgres metadata section missing"),
70 Self::MalformedMetadata(message) => write!(f, "postgres metadata layout: {message}"),
71 Self::MissingReverseIndex => {
72 f.write_str("artifact missing HAS_REVERSE_INDEX metadata flag")
73 }
74 Self::TopologyNodeCountMismatch => {
75 f.write_str("forward and inbound node counts diverged")
76 }
77 Self::MetadataNodeCountMismatch => {
78 f.write_str("forward node count disagrees with metadata")
79 }
80 Self::MetadataEdgeCountMismatch => {
81 f.write_str("forward edge count disagrees with metadata")
82 }
83 Self::Spi(message) => write!(f, "spi: {message}"),
84 }
85 }
86}
87
88impl core::error::Error for BuildError {}
89
90#[derive(Debug, Clone, PartialEq, Eq)]
92pub enum QueryError {
93 SeedOutOfBounds {
95 seed: u32,
97 node_count: u32,
99 },
100 LimitZero,
102 NodeIndexOverflow,
104 InternalInvariant(&'static str),
106}
107
108impl fmt::Display for QueryError {
109 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110 match self {
111 Self::SeedOutOfBounds { seed, node_count } => {
112 write!(f, "seed {seed} out of bounds (node_count {node_count})")
113 }
114 Self::LimitZero => f.write_str("limit must be > 0"),
115 Self::NodeIndexOverflow => f.write_str("node index does not fit in u32"),
116 Self::InternalInvariant(message) => write!(f, "internal query invariant: {message}"),
117 }
118 }
119}
120
121impl core::error::Error for QueryError {}
122
123#[derive(Debug, Clone, Copy, PartialEq, Eq)]
125pub enum SyncError {
126 NonMonotonicSequence {
128 sequence: u64,
130 previous: u64,
132 },
133 InvalidActionType {
135 action_type: i16,
137 },
138 InvalidActionArgs {
140 action_type: i16,
142 },
143 UnknownNodeKey {
145 key: crate::catalog::NodeKey,
147 },
148}
149
150impl fmt::Display for SyncError {
151 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152 match self {
153 Self::NonMonotonicSequence { sequence, previous } => {
154 write!(f, "non-monotonic sync sequence {sequence} after {previous}")
155 }
156 Self::InvalidActionType { action_type } => {
157 write!(f, "invalid sync action type {action_type}")
158 }
159 Self::InvalidActionArgs { action_type } => {
160 write!(f, "invalid sync action arguments for type {action_type}")
161 }
162 Self::UnknownNodeKey { key } => write!(f, "unknown sync node key {key}"),
163 }
164 }
165}
166
167impl core::error::Error for SyncError {}
168
169#[derive(Debug, Clone, PartialEq, Eq)]
171pub enum ConfigError {
172 ZeroTraverseLimit,
174 ZeroSearchLimit,
176 MaintenanceDisabled,
178}
179
180impl fmt::Display for ConfigError {
181 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
182 match self {
183 Self::ZeroTraverseLimit => f.write_str("traverse_limit must be > 0"),
184 Self::ZeroSearchLimit => f.write_str("search_limit must be > 0"),
185 Self::MaintenanceDisabled => f.write_str("maintenance rebuild disabled by config"),
186 }
187 }
188}
189
190impl core::error::Error for ConfigError {}
191
192#[derive(Debug)]
196pub enum PostgresGraphError {
197 Snapshot(SnapshotError),
199 ForwardSnapshot(CsrSnapshotError<u32, u32>),
201 InboundSnapshot(CscSnapshotError),
203 NotLoaded,
205 Build(BuildError),
207 Catalog(CatalogError),
209 Query(QueryError),
211 Sync(SyncError),
213 Config(ConfigError),
215 AccessDenied {
217 required: GraphRole,
219 actual: GraphRole,
221 },
222}
223
224impl fmt::Display for PostgresGraphError {
225 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
226 match self {
227 Self::Snapshot(error) => write!(f, "snapshot error: {error}"),
228 Self::ForwardSnapshot(error) => write!(f, "forward snapshot error: {error}"),
229 Self::InboundSnapshot(error) => write!(f, "inbound snapshot error: {error}"),
230 Self::NotLoaded => f.write_str("graph engine not loaded"),
231 Self::Build(error) => write!(f, "build error: {error}"),
232 Self::Catalog(error) => write!(f, "catalog error: {error}"),
233 Self::Query(error) => write!(f, "query error: {error}"),
234 Self::Sync(error) => write!(f, "sync error: {error}"),
235 Self::Config(error) => write!(f, "config error: {error}"),
236 Self::AccessDenied { required, actual } => {
237 write!(f, "access denied: {actual:?} cannot satisfy {required:?}")
238 }
239 }
240 }
241}
242
243impl core::error::Error for PostgresGraphError {}
244
245impl From<SnapshotError> for PostgresGraphError {
246 fn from(error: SnapshotError) -> Self {
247 Self::Snapshot(error)
248 }
249}
250
251impl From<CsrSnapshotError<u32, u32>> for PostgresGraphError {
252 fn from(error: CsrSnapshotError<u32, u32>) -> Self {
253 Self::ForwardSnapshot(error)
254 }
255}
256
257impl From<CscSnapshotError> for PostgresGraphError {
258 fn from(error: CscSnapshotError) -> Self {
259 Self::InboundSnapshot(error)
260 }
261}
262
263impl From<PlanError> for PostgresGraphError {
264 fn from(error: PlanError) -> Self {
265 Self::Build(BuildError::Plan(error))
266 }
267}
268
269impl From<oxgraph_csr::build::GraphBuildError<u32, u32>> for PostgresGraphError {
270 fn from(error: oxgraph_csr::build::GraphBuildError<u32, u32>) -> Self {
271 Self::Build(BuildError::Graph(error))
272 }
273}
274
275impl From<CatalogError> for PostgresGraphError {
276 fn from(error: CatalogError) -> Self {
277 Self::Catalog(error)
278 }
279}
280
281impl From<BuildError> for PostgresGraphError {
282 fn from(error: BuildError) -> Self {
283 Self::Build(error)
284 }
285}
286
287impl From<QueryError> for PostgresGraphError {
288 fn from(error: QueryError) -> Self {
289 Self::Query(error)
290 }
291}
292
293impl From<SyncError> for PostgresGraphError {
294 fn from(error: SyncError) -> Self {
295 Self::Sync(error)
296 }
297}
298
299impl From<ConfigError> for PostgresGraphError {
300 fn from(error: ConfigError) -> Self {
301 Self::Config(error)
302 }
303}