1#[derive(Debug, PartialEq, thiserror::Error)]
7pub enum SqlError {
8 #[error("parse error: {detail}")]
9 Parse { detail: String },
10
11 #[error("table not found: {name}")]
12 UnknownTable { name: String },
13
14 #[error("unknown column '{column}' in table '{table}'")]
15 UnknownColumn { table: String, column: String },
16
17 #[error("ambiguous column '{column}' — qualify with table name")]
18 AmbiguousColumn { column: String },
19
20 #[error("type mismatch: {detail}")]
21 TypeMismatch { detail: String },
22
23 #[error("unsupported: {detail}")]
24 Unsupported { detail: String },
25
26 #[error("invalid function call: {detail}")]
27 InvalidFunction { detail: String },
28
29 #[error("invalid window frame: {detail}")]
30 InvalidWindowFrame { detail: String },
31
32 #[error("missing required field '{field}' for {context}")]
33 MissingField { field: String, context: String },
34
35 #[error("retryable schema change on {descriptor}")]
40 RetryableSchemaChanged { descriptor: String },
41
42 #[error(
44 "identifier '{name}' is reserved by NodeDB ({reason}); \
45 use a quoted identifier (e.g., \"{name}\") to bypass"
46 )]
47 ReservedIdentifier { name: String, reason: &'static str },
48
49 #[error("unsupported constraint: {feature}; {hint}")]
54 UnsupportedConstraint { feature: String, hint: String },
55
56 #[error(
62 "WITH RECURSIVE: only UNION / UNION ALL are allowed in the recursive term; \
63 {op} is not permitted"
64 )]
65 InvalidRecursiveSetOp { op: String },
66
67 #[error("WITH RECURSIVE: invalid self-reference to '{cte_name}' in recursive term: {reason}")]
70 InvalidRecursiveSelfRef { cte_name: String, reason: String },
71
72 #[error(
75 "WITH RECURSIVE CTE '{cte_name}': anchor produces {anchor_cols} column(s) \
76 but {declared_cols} were declared"
77 )]
78 RecursiveColumnMismatch {
79 cte_name: String,
80 anchor_cols: usize,
81 declared_cols: usize,
82 },
83
84 #[error(
88 "WITH RECURSIVE CTE '{cte_name}' exceeded max recursion depth {max_depth}; \
89 add a stricter termination condition or raise max_recursion_depth"
90 )]
91 RecursionDepthExceeded { cte_name: String, max_depth: usize },
92
93 #[error(
98 "collection '{name}' was dropped; \
99 restore with `{undrop_hint}` before retention elapses \
100 at {retention_expires_at_ns} ns"
101 )]
102 CollectionDeactivated {
103 name: String,
104 retention_expires_at_ns: u64,
105 undrop_hint: String,
106 },
107}
108
109impl From<crate::catalog::SqlCatalogError> for SqlError {
110 fn from(e: crate::catalog::SqlCatalogError) -> Self {
111 match e {
112 crate::catalog::SqlCatalogError::RetryableSchemaChanged { descriptor } => {
113 Self::RetryableSchemaChanged { descriptor }
114 }
115 crate::catalog::SqlCatalogError::CollectionDeactivated {
116 name,
117 retention_expires_at_ns,
118 } => {
119 let undrop_hint = format!("UNDROP COLLECTION {name}");
120 Self::CollectionDeactivated {
121 name,
122 retention_expires_at_ns,
123 undrop_hint,
124 }
125 }
126 }
127 }
128}
129
130impl From<nodedb_query::expr_parse::ExprParseError> for SqlError {
131 fn from(e: nodedb_query::expr_parse::ExprParseError) -> Self {
132 Self::Parse {
133 detail: e.to_string(),
134 }
135 }
136}
137
138impl From<sqlparser::parser::ParserError> for SqlError {
139 fn from(e: sqlparser::parser::ParserError) -> Self {
140 Self::Parse {
141 detail: e.to_string(),
142 }
143 }
144}
145
146pub type Result<T> = std::result::Result<T, SqlError>;