1use std::error::Error;
15use std::sync::Arc;
16use thiserror::Error;
17
18pub mod codes;
20pub mod manager;
21pub mod query;
22pub mod storage;
23
24pub use codes::{ErrorCategory as CodeErrorCategory, ErrorCode, PublicError, ToPublicError};
26
27pub use manager::{ErrorCategory, ManagerError, ManagerResult};
29pub use query::PlanNodeVisitError;
30pub use query::QueryError;
31pub use query::QueryResult;
32pub use storage::StorageError;
33pub use storage::StorageResult;
34
35pub use crate::types::DataType;
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq)]
41pub enum ErrorClass {
42 Retryable,
44 UserError,
46 SystemError,
48 Fatal,
50}
51
52impl ErrorClass {
53 pub fn is_retryable(&self) -> bool {
54 matches!(self, ErrorClass::Retryable)
55 }
56
57 pub fn is_user_error(&self) -> bool {
58 matches!(self, ErrorClass::UserError)
59 }
60
61 pub fn is_system_error(&self) -> bool {
62 matches!(self, ErrorClass::SystemError | ErrorClass::Fatal)
63 }
64}
65
66pub type BoxedError = Box<dyn Error + Send + Sync>;
70
71pub type SharedError = Arc<dyn Error + Send + Sync>;
73
74#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
78pub enum ErrorKind {
79 Storage,
80 Query,
81 Expression,
82 Plan,
83 Manager,
84 Validation,
85 Io,
86 TypeDeduction,
87 Serialization,
88 Index,
89 Transaction,
90 GraphService,
91 Internal,
92 Session,
93 Auth,
94 Permission,
95 MemoryLimitExceeded,
96 Fulltext,
97 Coordinator,
98 Vector,
99 VectorCoordinator,
100 Search,
101}
102
103impl ErrorKind {
104 pub fn as_str(&self) -> &'static str {
105 match self {
106 ErrorKind::Storage => "storage",
107 ErrorKind::Query => "query",
108 ErrorKind::Expression => "expression",
109 ErrorKind::Plan => "plan",
110 ErrorKind::Manager => "manager",
111 ErrorKind::Validation => "validation",
112 ErrorKind::Io => "io",
113 ErrorKind::TypeDeduction => "type_deduction",
114 ErrorKind::Serialization => "serialization",
115 ErrorKind::Index => "index",
116 ErrorKind::Transaction => "transaction",
117 ErrorKind::GraphService => "graph_service",
118 ErrorKind::Internal => "internal",
119 ErrorKind::Session => "session",
120 ErrorKind::Auth => "auth",
121 ErrorKind::Permission => "permission",
122 ErrorKind::MemoryLimitExceeded => "memory_limit_exceeded",
123 ErrorKind::Fulltext => "fulltext",
124 ErrorKind::Coordinator => "coordinator",
125 ErrorKind::Vector => "vector",
126 ErrorKind::VectorCoordinator => "vector_coordinator",
127 ErrorKind::Search => "search",
128 }
129 }
130}
131
132#[derive(Error, Debug)]
141pub struct DBError {
142 kind: ErrorKind,
143 message: String,
144 #[source]
145 source: Option<BoxedError>,
146 class: ErrorClass,
147}
148
149impl DBError {
150 pub fn new(kind: ErrorKind, message: impl Into<String>) -> Self {
151 let class = kind.default_class();
152 Self {
153 kind,
154 message: message.into(),
155 source: None,
156 class,
157 }
158 }
159
160 pub fn with_source(mut self, source: BoxedError) -> Self {
161 self.source = Some(source);
162 self
163 }
164
165 pub fn with_class(mut self, class: ErrorClass) -> Self {
166 self.class = class;
167 self
168 }
169
170 pub fn kind(&self) -> ErrorKind {
171 self.kind
172 }
173
174 pub fn class(&self) -> ErrorClass {
175 self.class
176 }
177
178 pub fn message(&self) -> &str {
179 &self.message
180 }
181
182 pub fn is_retryable(&self) -> bool {
183 self.class.is_retryable()
184 }
185
186 pub fn is_user_error(&self) -> bool {
187 self.class.is_user_error()
188 }
189
190 pub fn is_system_error(&self) -> bool {
191 self.class.is_system_error()
192 }
193
194 pub fn source(&self) -> &Option<BoxedError> {
195 &self.source
196 }
197}
198
199impl std::fmt::Display for DBError {
200 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
201 write!(f, "[{}] {}", self.kind.as_str(), self.message)?;
202 if let Some(ref source) = self.source {
203 write!(f, "\n Caused by: {}", source)?;
204 }
205 Ok(())
206 }
207}
208
209impl Clone for DBError {
210 fn clone(&self) -> Self {
211 Self {
212 kind: self.kind,
213 message: self.message.clone(),
214 source: None,
215 class: self.class,
216 }
217 }
218}
219
220impl ErrorKind {
221 fn default_class(&self) -> ErrorClass {
222 match self {
223 ErrorKind::Storage => ErrorClass::SystemError,
224 ErrorKind::Query => ErrorClass::UserError,
225 ErrorKind::Expression => ErrorClass::UserError,
226 ErrorKind::Plan => ErrorClass::UserError,
227 ErrorKind::Manager => ErrorClass::SystemError,
228 ErrorKind::Validation => ErrorClass::UserError,
229 ErrorKind::Io => ErrorClass::SystemError,
230 ErrorKind::TypeDeduction => ErrorClass::UserError,
231 ErrorKind::Serialization => ErrorClass::SystemError,
232 ErrorKind::Index => ErrorClass::SystemError,
233 ErrorKind::Transaction => ErrorClass::SystemError,
234 ErrorKind::GraphService => ErrorClass::SystemError,
235 ErrorKind::Internal => ErrorClass::SystemError,
236 ErrorKind::Session => ErrorClass::UserError,
237 ErrorKind::Auth => ErrorClass::UserError,
238 ErrorKind::Permission => ErrorClass::UserError,
239 ErrorKind::MemoryLimitExceeded => ErrorClass::SystemError,
240 ErrorKind::Fulltext => ErrorClass::SystemError,
241 ErrorKind::Coordinator => ErrorClass::SystemError,
242 ErrorKind::Vector => ErrorClass::SystemError,
243 ErrorKind::VectorCoordinator => ErrorClass::SystemError,
244 ErrorKind::Search => ErrorClass::SystemError,
245 }
246 }
247}
248
249impl DBError {
252 pub fn storage(message: impl Into<String>) -> Self {
253 Self::new(ErrorKind::Storage, message)
254 }
255
256 pub fn query(message: impl Into<String>) -> Self {
257 Self::new(ErrorKind::Query, message)
258 }
259
260 pub fn validation(message: impl Into<String>) -> Self {
261 Self::new(ErrorKind::Validation, message)
262 }
263
264 pub fn io(message: impl Into<String>) -> Self {
265 Self::new(ErrorKind::Io, message)
266 }
267
268 pub fn internal(message: impl Into<String>) -> Self {
269 Self::new(ErrorKind::Internal, message)
270 }
271
272 pub fn transaction(message: impl Into<String>) -> Self {
273 Self::new(ErrorKind::Transaction, message)
274 }
275
276 pub fn search(message: impl Into<String>) -> Self {
277 Self::new(ErrorKind::Search, message)
278 }
279
280 pub fn expression(message: impl Into<String>) -> Self {
281 Self::new(ErrorKind::Expression, message)
282 }
283
284 pub fn plan(message: impl Into<String>) -> Self {
285 Self::new(ErrorKind::Plan, message)
286 }
287
288 pub fn manager(message: impl Into<String>) -> Self {
289 Self::new(ErrorKind::Manager, message)
290 }
291
292 pub fn session(message: impl Into<String>) -> Self {
293 Self::new(ErrorKind::Session, message)
294 }
295
296 pub fn auth(message: impl Into<String>) -> Self {
297 Self::new(ErrorKind::Auth, message)
298 }
299
300 pub fn permission(message: impl Into<String>) -> Self {
301 Self::new(ErrorKind::Permission, message)
302 }
303
304 pub fn fulltext(message: impl Into<String>) -> Self {
305 Self::new(ErrorKind::Fulltext, message)
306 }
307
308 pub fn vector(message: impl Into<String>) -> Self {
309 Self::new(ErrorKind::Vector, message)
310 }
311
312 pub fn index(message: impl Into<String>) -> Self {
313 Self::new(ErrorKind::Index, message)
314 }
315}
316
317impl From<serde_json::Error> for DBError {
320 fn from(err: serde_json::Error) -> Self {
321 DBError::new(ErrorKind::Serialization, err.to_string())
322 }
323}
324
325impl From<std::io::Error> for DBError {
326 fn from(err: std::io::Error) -> Self {
327 DBError::io(err.to_string())
328 }
329}
330
331impl From<query::QueryError> for DBError {
332 fn from(err: query::QueryError) -> Self {
333 DBError::query(err.to_string()).with_source(Box::new(err))
334 }
335}
336
337impl From<storage::StorageError> for DBError {
338 fn from(err: storage::StorageError) -> Self {
339 let class = if err.is_retryable() {
340 ErrorClass::Retryable
341 } else {
342 ErrorClass::SystemError
343 };
344 DBError::storage(err.to_string())
345 .with_source(Box::new(err))
346 .with_class(class)
347 }
348}
349
350impl ToPublicError for DBError {
353 fn to_public_error(&self) -> PublicError {
354 PublicError::new(self.to_error_code(), self.to_public_message())
355 }
356
357 fn to_error_code(&self) -> ErrorCode {
358 match self.kind {
359 ErrorKind::Storage => {
360 if let Some(ref source) = self.source {
361 if let Some(se) = source.downcast_ref::<StorageError>() {
362 return se.to_error_code();
363 }
364 }
365 ErrorCode::InternalError
366 }
367 ErrorKind::Query => {
368 if let Some(ref source) = self.source {
369 if let Some(qe) = source.downcast_ref::<QueryError>() {
370 return qe.to_error_code();
371 }
372 }
373 ErrorCode::ExecutionError
374 }
375 ErrorKind::Expression | ErrorKind::Plan => ErrorCode::ExecutionError,
376 ErrorKind::Manager => {
377 if let Some(ref source) = self.source {
378 if let Some(me) = source.downcast_ref::<ManagerError>() {
379 return me.to_error_code();
380 }
381 }
382 ErrorCode::InternalError
383 }
384 ErrorKind::Validation | ErrorKind::TypeDeduction => ErrorCode::ValidationError,
385 ErrorKind::Io | ErrorKind::Serialization | ErrorKind::Index => ErrorCode::InternalError,
386 ErrorKind::Transaction | ErrorKind::GraphService => ErrorCode::ExecutionError,
387 ErrorKind::Internal => ErrorCode::InternalError,
388 ErrorKind::Session => ErrorCode::Unauthorized,
389 ErrorKind::Auth => ErrorCode::Unauthorized,
390 ErrorKind::Permission => ErrorCode::PermissionDenied,
391 ErrorKind::MemoryLimitExceeded => ErrorCode::ResourceExhausted,
392 ErrorKind::Fulltext | ErrorKind::Coordinator => ErrorCode::ExecutionError,
393 ErrorKind::Vector | ErrorKind::VectorCoordinator => ErrorCode::ExecutionError,
394 ErrorKind::Search => ErrorCode::ExecutionError,
395 }
396 }
397
398 fn to_public_message(&self) -> String {
399 match self.kind {
400 ErrorKind::Internal => "Internal server error".to_string(),
401 ErrorKind::Io => "IO operation failed".to_string(),
402 ErrorKind::Serialization => "Data serialization failed".to_string(),
403 ErrorKind::Index => "Index operation failed".to_string(),
404 ErrorKind::GraphService => "Graph service error".to_string(),
405 _ => self.message.clone(),
406 }
407 }
408}
409
410pub type DBResult<T> = Result<T, DBError>;
414
415#[cfg(test)]
421mod tests {
422 use super::*;
423
424 #[test]
425 fn test_dberror_size() {
426 assert!(
427 std::mem::size_of::<DBError>() <= 64,
428 "DBError should be small"
429 );
430 }
431
432 #[test]
433 fn test_dberror_creation() {
434 let storage_err = StorageError::node_not_found(
435 crate::types::VertexId::try_from_int64(42).expect("test vertex id"),
436 );
437 let db_err: DBError = storage_err.into();
438 assert_eq!(db_err.kind(), ErrorKind::Storage);
439 assert!(!db_err.is_retryable());
440 }
441
442 #[test]
443 fn test_error_conversion() {
444 let query_err = QueryError::parse_error("test error");
445 let db_err: DBError = query_err.into();
446 assert_eq!(db_err.kind(), ErrorKind::Query);
447 assert!(db_err.is_user_error());
448 }
449
450 #[test]
451 fn test_error_class() {
452 let err = DBError::validation("test");
453 assert!(err.is_user_error());
454 assert!(!err.is_retryable());
455 }
456
457 #[test]
458 fn test_retryable_error() {
459 let storage_err = StorageError::lock_timeout("test".to_string());
460 let db_err: DBError = storage_err.into();
461 assert!(db_err.is_retryable());
462 }
463
464 #[test]
465 fn test_error_display() {
466 let err = DBError::query("test query error");
467 let display = format!("{}", err);
468 assert!(display.contains("query"));
469 assert!(display.contains("test query error"));
470 }
471}