1use std::error::Error;
11
12use super::BoxedError;
13use crate::error::codes::{ErrorCode, PublicError, ToPublicError};
14use crate::error::manager::ManagerError;
15use crate::error::storage::StorageError;
16use crate::error::DBError;
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub enum QueryPhase {
23 Parse,
24 Validate,
25 Plan,
26 Optimize,
27 Execute,
28}
29
30impl std::fmt::Display for QueryPhase {
31 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32 match self {
33 QueryPhase::Parse => write!(f, "parse"),
34 QueryPhase::Validate => write!(f, "validate"),
35 QueryPhase::Plan => write!(f, "plan"),
36 QueryPhase::Optimize => write!(f, "optimize"),
37 QueryPhase::Execute => write!(f, "execute"),
38 }
39 }
40}
41
42#[derive(Debug, Clone)]
46pub enum PlanNodeVisitError {
47 VisitError {
48 node_id: Option<String>,
49 message: String,
50 },
51 TraversalError {
52 path: String,
53 message: String,
54 },
55 ValidationError {
56 node_type: String,
57 message: String,
58 },
59}
60
61impl std::fmt::Display for PlanNodeVisitError {
62 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63 match self {
64 PlanNodeVisitError::VisitError { node_id, message } => {
65 if let Some(id) = node_id {
66 write!(f, "Visit error at node {}: {}", id, message)
67 } else {
68 write!(f, "Visit error: {}", message)
69 }
70 }
71 PlanNodeVisitError::TraversalError { path, message } => {
72 write!(f, "Traversal error in {}: {}", path, message)
73 }
74 PlanNodeVisitError::ValidationError { node_type, message } => {
75 write!(f, "Validation failed for {}: {}", node_type, message)
76 }
77 }
78 }
79}
80
81impl std::error::Error for PlanNodeVisitError {}
82
83impl PlanNodeVisitError {
84 pub fn visit_error(message: impl Into<String>) -> Self {
85 PlanNodeVisitError::VisitError {
86 node_id: None,
87 message: message.into(),
88 }
89 }
90
91 pub fn visit_error_with_node(node_id: impl Into<String>, message: impl Into<String>) -> Self {
92 PlanNodeVisitError::VisitError {
93 node_id: Some(node_id.into()),
94 message: message.into(),
95 }
96 }
97
98 pub fn traversal_error(path: impl Into<String>, message: impl Into<String>) -> Self {
99 PlanNodeVisitError::TraversalError {
100 path: path.into(),
101 message: message.into(),
102 }
103 }
104
105 pub fn validation_error(node_type: impl Into<String>, message: impl Into<String>) -> Self {
106 PlanNodeVisitError::ValidationError {
107 node_type: node_type.into(),
108 message: message.into(),
109 }
110 }
111}
112
113pub type QueryResult<T> = Result<T, QueryError>;
115
116#[derive(Debug, Clone, PartialEq, Eq)]
120pub struct StructuredParseError {
121 pub kind: ParseErrorKind,
123 pub message: String,
125 pub position: crate::types::Position,
127 pub offset: Option<usize>,
129 pub unexpected_token: Option<String>,
131 pub expected_tokens: Vec<String>,
133 pub hints: Vec<String>,
135 pub context: Option<String>,
137 pub recovered: bool,
140}
141
142#[derive(Debug, Clone, Copy, PartialEq, Eq)]
144pub enum ParseErrorKind {
145 LexicalError,
146 SyntaxError,
147 UnexpectedToken,
148 UnterminatedString,
149 UnterminatedComment,
150 InvalidNumber,
151 InvalidEscapeSequence,
152 UnicodeEscapeError,
153 UnexpectedEndOfInput,
154 InvalidCharacter,
155 UnknownKeyword,
156 RecursionLimitExceeded,
157 UnsupportedFeature,
158 SemanticError,
159}
160
161impl std::fmt::Display for ParseErrorKind {
162 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
163 match self {
164 ParseErrorKind::LexicalError => write!(f, "Lexical error"),
165 ParseErrorKind::SyntaxError => write!(f, "Syntax error"),
166 ParseErrorKind::UnexpectedToken => write!(f, "Unexpected token"),
167 ParseErrorKind::UnterminatedString => write!(f, "Unterminated string"),
168 ParseErrorKind::UnterminatedComment => write!(f, "Unterminated comment"),
169 ParseErrorKind::InvalidNumber => write!(f, "Invalid number"),
170 ParseErrorKind::InvalidEscapeSequence => write!(f, "Invalid escape sequence"),
171 ParseErrorKind::UnicodeEscapeError => write!(f, "Unicode escape error"),
172 ParseErrorKind::UnexpectedEndOfInput => write!(f, "Unexpected end of input"),
173 ParseErrorKind::InvalidCharacter => write!(f, "Invalid character"),
174 ParseErrorKind::UnknownKeyword => write!(f, "Unknown keyword"),
175 ParseErrorKind::RecursionLimitExceeded => write!(f, "Recursion limit exceeded"),
176 ParseErrorKind::UnsupportedFeature => write!(f, "Unsupported feature"),
177 ParseErrorKind::SemanticError => write!(f, "Semantic error"),
178 }
179 }
180}
181
182impl std::fmt::Display for StructuredParseError {
183 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
184 write!(
185 f,
186 "{} at line {}, column {}: {}",
187 self.kind, self.position.line, self.position.column, self.message
188 )?;
189
190 if let Some(ref token) = self.unexpected_token {
191 writeln!(f, "\n Unexpected token: {}", token)?;
192 }
193
194 if !self.expected_tokens.is_empty() {
195 writeln!(
196 f,
197 "\n Expected one of: {}",
198 self.expected_tokens.join(", ")
199 )?;
200 }
201
202 if let Some(ref context) = self.context {
203 writeln!(f, "\n Context: {}", context)?;
204 }
205
206 if !self.hints.is_empty() {
207 writeln!(f, "\n Hint(s):")?;
208 for hint in &self.hints {
209 writeln!(f, " - {}", hint)?;
210 }
211 }
212
213 if self.recovered {
214 writeln!(f, "\n (recovered, parsing continued)")?;
215 }
216
217 Ok(())
218 }
219}
220
221impl std::error::Error for StructuredParseError {}
222
223#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
225pub enum QueryErrorKind {
226 Storage,
227 Parse,
228 Planning,
229 Optimization,
230 InvalidQuery,
231 Execution,
232 Expression,
233 PlanNodeVisit,
234 Session,
235 Permission,
236 Transaction,
237 Type,
238 Timeout,
239 FeatureDisabled,
240}
241
242impl QueryErrorKind {
243 pub fn as_str(&self) -> &'static str {
244 match self {
245 QueryErrorKind::Storage => "storage",
246 QueryErrorKind::Parse => "parse",
247 QueryErrorKind::Planning => "planning",
248 QueryErrorKind::Optimization => "optimization",
249 QueryErrorKind::InvalidQuery => "invalid_query",
250 QueryErrorKind::Execution => "execution",
251 QueryErrorKind::Expression => "expression",
252 QueryErrorKind::PlanNodeVisit => "plan_node_visit",
253 QueryErrorKind::Session => "session",
254 QueryErrorKind::Permission => "permission",
255 QueryErrorKind::Transaction => "transaction",
256 QueryErrorKind::Type => "type",
257 QueryErrorKind::Timeout => "timeout",
258 QueryErrorKind::FeatureDisabled => "feature_disabled",
259 }
260 }
261}
262
263#[derive(Debug)]
270pub struct QueryError {
271 kind: QueryErrorKind,
272 message: String,
273 source: Option<BoxedError>,
274}
275
276impl QueryError {
277 pub fn new(kind: QueryErrorKind, message: impl Into<String>) -> Self {
278 Self {
279 kind,
280 message: message.into(),
281 source: None,
282 }
283 }
284
285 pub fn with_source(mut self, source: BoxedError) -> Self {
286 self.source = Some(source);
287 self
288 }
289
290 pub fn kind(&self) -> QueryErrorKind {
291 self.kind
292 }
293
294 pub fn message(&self) -> &str {
295 &self.message
296 }
297
298 pub fn source(&self) -> &Option<BoxedError> {
299 &self.source
300 }
301
302 fn from_boxed<E: Error + Send + Sync + 'static>(kind: QueryErrorKind, error: E) -> Self {
303 Self {
304 kind,
305 message: error.to_string(),
306 source: Some(Box::new(error)),
307 }
308 }
309}
310
311impl std::fmt::Display for QueryError {
312 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
313 write!(f, "[{}] {}", self.kind.as_str(), self.message)?;
314 if let Some(ref source) = self.source {
315 write!(f, "\n Caused by: {}", source)?;
316 }
317 Ok(())
318 }
319}
320
321impl Clone for QueryError {
322 fn clone(&self) -> Self {
323 Self {
324 kind: self.kind,
325 message: self.message.clone(),
326 source: None,
327 }
328 }
329}
330
331impl std::error::Error for QueryError {
332 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
333 self.source
334 .as_ref()
335 .map(|e| e.as_ref() as &(dyn std::error::Error + 'static))
336 }
337}
338
339impl QueryError {
340 pub fn storage(message: impl Into<String>) -> Self {
341 Self::new(QueryErrorKind::Storage, message)
342 }
343
344 pub fn parse(message: impl Into<String>) -> Self {
345 Self::new(QueryErrorKind::Parse, message)
346 }
347
348 pub fn planning(message: impl Into<String>) -> Self {
349 Self::new(QueryErrorKind::Planning, message)
350 }
351
352 pub fn optimization(message: impl Into<String>) -> Self {
353 Self::new(QueryErrorKind::Optimization, message)
354 }
355
356 pub fn invalid_query(message: impl Into<String>) -> Self {
357 Self::new(QueryErrorKind::InvalidQuery, message)
358 }
359
360 pub fn execution(message: impl Into<String>) -> Self {
361 Self::new(QueryErrorKind::Execution, message)
362 }
363
364 pub fn expression(message: impl Into<String>) -> Self {
365 Self::new(QueryErrorKind::Expression, message)
366 }
367
368 pub fn transaction(message: impl Into<String>) -> Self {
369 Self::new(QueryErrorKind::Transaction, message)
370 }
371
372 pub fn type_error(message: impl Into<String>) -> Self {
373 Self::new(QueryErrorKind::Type, message)
374 }
375
376 pub fn timeout(message: impl Into<String>) -> Self {
377 Self::new(QueryErrorKind::Timeout, message)
378 }
379
380 pub fn feature_disabled(feature: &str, operation: &str) -> Self {
383 Self::new(
384 QueryErrorKind::FeatureDisabled,
385 format!(
386 "operation {operation} requires the \"{feature}\" feature, which is not enabled in this build"
387 ),
388 )
389 }
390
391 pub fn structured_parse_error(err: StructuredParseError) -> Self {
392 Self::from_boxed(QueryErrorKind::Parse, err)
393 }
394
395 pub fn parse_error(message: impl Into<String>) -> Self {
396 Self::structured_parse_error(StructuredParseError {
397 kind: ParseErrorKind::SyntaxError,
398 message: message.into(),
399 position: crate::types::Position::new(0, 0),
400 offset: None,
401 unexpected_token: None,
402 expected_tokens: Vec::new(),
403 hints: Vec::new(),
404 context: None,
405 recovered: false,
406 })
407 }
408
409 pub fn parse_error_with_offset(message: impl Into<String>, offset: usize) -> Self {
410 Self::structured_parse_error(StructuredParseError {
411 kind: ParseErrorKind::SyntaxError,
412 message: message.into(),
413 position: crate::types::Position::new(0, 0),
414 offset: Some(offset),
415 unexpected_token: None,
416 expected_tokens: Vec::new(),
417 hints: Vec::new(),
418 context: None,
419 recovered: false,
420 })
421 }
422
423 pub fn parse_error_with_location(
424 message: impl Into<String>,
425 offset: usize,
426 location: impl Into<String>,
427 ) -> Self {
428 Self::structured_parse_error(StructuredParseError {
429 kind: ParseErrorKind::SyntaxError,
430 message: message.into(),
431 position: crate::types::Position::new(0, 0),
432 offset: Some(offset),
433 unexpected_token: None,
434 expected_tokens: Vec::new(),
435 hints: vec![location.into()],
436 context: None,
437 recovered: false,
438 })
439 }
440
441 pub fn offset(&self) -> Option<usize> {
442 if self.kind == QueryErrorKind::Parse {
443 if let Some(ref source) = self.source {
444 if let Some(pe) = source.downcast_ref::<StructuredParseError>() {
445 return pe.offset;
446 }
447 }
448 }
449 None
450 }
451
452 pub fn location(&self) -> Option<&str> {
453 if self.kind == QueryErrorKind::Parse {
454 if let Some(ref source) = self.source {
455 if let Some(pe) = source.downcast_ref::<StructuredParseError>() {
456 if !pe.hints.is_empty() {
457 return Some(&pe.hints[0]);
458 }
459 }
460 }
461 }
462 None
463 }
464
465 pub fn parse_error_position(&self) -> Option<crate::types::Position> {
466 if self.kind == QueryErrorKind::Parse {
467 if let Some(ref source) = self.source {
468 if let Some(pe) = source.downcast_ref::<StructuredParseError>() {
469 return Some(pe.position);
470 }
471 }
472 }
473 None
474 }
475
476 pub fn parse_error_kind(&self) -> Option<ParseErrorKind> {
477 if self.kind == QueryErrorKind::Parse {
478 if let Some(ref source) = self.source {
479 if let Some(pe) = source.downcast_ref::<StructuredParseError>() {
480 return Some(pe.kind);
481 }
482 }
483 }
484 None
485 }
486
487 pub fn pipeline_parse_error<E: std::error::Error + Send + Sync + 'static>(e: E) -> Self {
488 Self::from_boxed(QueryErrorKind::Parse, e)
489 }
490
491 pub fn pipeline_validation_error<E: std::error::Error + Send + Sync + 'static>(e: E) -> Self {
492 Self::from_boxed(QueryErrorKind::InvalidQuery, e)
493 }
494
495 pub fn pipeline_planning_error<E: std::error::Error + Send + Sync + 'static>(e: E) -> Self {
496 Self::from_boxed(QueryErrorKind::Planning, e)
497 }
498
499 pub fn pipeline_optimization_error<E: std::error::Error + Send + Sync + 'static>(e: E) -> Self {
500 Self::from_boxed(QueryErrorKind::Optimization, e)
501 }
502
503 pub fn pipeline_execution_error<E: std::error::Error + Send + Sync + 'static>(e: E) -> Self {
504 Self::from_boxed(QueryErrorKind::Execution, e)
505 }
506
507 pub fn pipeline_error(phase: QueryPhase, message: String) -> Self {
508 match phase {
509 QueryPhase::Parse => Self::parse(message),
510 QueryPhase::Validate => Self::invalid_query(message),
511 QueryPhase::Plan => Self::planning(message),
512 QueryPhase::Optimize => Self::optimization(message),
513 QueryPhase::Execute => Self::execution(message),
514 }
515 }
516}
517
518impl From<StorageError> for QueryError {
519 fn from(e: StorageError) -> Self {
520 Self::from_boxed(QueryErrorKind::Storage, e)
521 }
522}
523
524impl From<DBError> for QueryError {
525 fn from(e: DBError) -> Self {
526 use crate::error::ErrorKind;
527 match e.kind() {
528 ErrorKind::Query => {
529 if let Some(ref source) = e.source() {
530 if let Some(qe) = source.downcast_ref::<QueryError>() {
531 return qe.clone();
532 }
533 }
534 Self::execution(e.message().to_string())
535 }
536 ErrorKind::Storage => Self::storage(e.message()),
537 ErrorKind::Expression => Self::expression(e.message()),
538 ErrorKind::Plan => Self::execution(e.message()),
539 ErrorKind::Manager => Self::execution(e.message()),
540 ErrorKind::Validation => Self::invalid_query(e.message()),
541 ErrorKind::Io => Self::execution(e.message()),
542 ErrorKind::TypeDeduction => Self::type_error(e.message()),
543 ErrorKind::Serialization => Self::execution(e.message()),
544 ErrorKind::Index => Self::execution(e.message()),
545 ErrorKind::Transaction => Self::transaction(e.message()),
546 ErrorKind::Internal => Self::execution(e.message()),
547 ErrorKind::Session => Self::execution(e.message()),
548 ErrorKind::Auth => Self::execution(e.message()),
549 ErrorKind::Permission => Self::execution(e.message()),
550 ErrorKind::MemoryLimitExceeded => Self::execution(e.message()),
551 ErrorKind::Fulltext => Self::execution(e.message()),
552 ErrorKind::Coordinator => Self::execution(e.message()),
553 ErrorKind::Vector => Self::execution(e.message()),
554 ErrorKind::VectorCoordinator => Self::execution(e.message()),
555 ErrorKind::Search => Self::execution(e.message()),
556 ErrorKind::GraphService => Self::execution(e.message()),
557 }
558 }
559}
560
561impl From<std::io::Error> for QueryError {
562 fn from(e: std::io::Error) -> Self {
563 Self::execution(e.to_string())
564 }
565}
566
567impl From<PlanNodeVisitError> for QueryError {
568 fn from(e: PlanNodeVisitError) -> Self {
569 Self::from_boxed(QueryErrorKind::PlanNodeVisit, e)
570 }
571}
572
573impl From<ManagerError> for QueryError {
574 fn from(e: ManagerError) -> Self {
575 Self::execution(e.to_string())
576 }
577}
578
579impl ToPublicError for QueryError {
580 fn to_public_error(&self) -> PublicError {
581 PublicError::new(self.to_error_code(), self.to_public_message())
582 }
583
584 fn to_error_code(&self) -> ErrorCode {
585 match self.kind {
586 QueryErrorKind::Parse => ErrorCode::ParseError,
587 QueryErrorKind::InvalidQuery => ErrorCode::ValidationError,
588 QueryErrorKind::Planning => ErrorCode::ExecutionError,
589 QueryErrorKind::Optimization => ErrorCode::ExecutionError,
590 QueryErrorKind::Execution => ErrorCode::ExecutionError,
591 QueryErrorKind::Expression => ErrorCode::ExecutionError,
592 QueryErrorKind::Storage => ErrorCode::InternalError,
593 QueryErrorKind::PlanNodeVisit => ErrorCode::ExecutionError,
594 QueryErrorKind::Session => ErrorCode::Unauthorized,
595 QueryErrorKind::Permission => ErrorCode::PermissionDenied,
596 QueryErrorKind::Transaction => ErrorCode::ExecutionError,
597 QueryErrorKind::Type => ErrorCode::TypeError,
598 QueryErrorKind::Timeout => ErrorCode::Timeout,
599 QueryErrorKind::FeatureDisabled => ErrorCode::ValidationError,
600 }
601 }
602
603 fn to_public_message(&self) -> String {
604 match self.kind {
605 QueryErrorKind::Session => self.message.clone(),
606 QueryErrorKind::Permission => self.message.clone(),
607 QueryErrorKind::Storage => "Storage operation failed".to_string(),
608 _ => self.message.clone(),
609 }
610 }
611}
612
613#[cfg(test)]
614mod tests {
615 use super::*;
616
617 #[test]
618 fn test_queryerror_size() {
619 assert!(
620 std::mem::size_of::<QueryError>() <= 64,
621 "QueryError should be small, got {} bytes",
622 std::mem::size_of::<QueryError>()
623 );
624 }
625
626 #[test]
627 fn test_queryerror_creation() {
628 let err = QueryError::parse("test parse error");
629 assert_eq!(err.kind(), QueryErrorKind::Parse);
630 assert!(err.message().contains("test parse error"));
631 }
632
633 #[test]
634 fn test_queryerror_with_source() {
635 let storage_err = StorageError::node_not_found(
636 crate::types::VertexId::try_from_int64(42).expect("test vertex id"),
637 );
638 let query_err = QueryError::from(storage_err);
639 assert_eq!(query_err.kind(), QueryErrorKind::Storage);
640 }
641
642 #[test]
643 fn test_queryerror_clone() {
644 let err = QueryError::execution("test error");
645 let cloned = err.clone();
646 assert_eq!(err.kind(), cloned.kind());
647 assert_eq!(err.message(), cloned.message());
648 }
649
650 #[test]
651 fn test_pipeline_error() {
652 let err = QueryError::pipeline_error(QueryPhase::Parse, "parse failed".to_string());
653 assert_eq!(err.kind(), QueryErrorKind::Parse);
654
655 let err = QueryError::pipeline_error(QueryPhase::Execute, "exec failed".to_string());
656 assert_eq!(err.kind(), QueryErrorKind::Execution);
657 }
658}