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(
63 "conflicting engine selection: WITH (engine='{with_engine}') and \
64 ENGINE = {suffix_engine} name different engines; only one engine-selection \
65 clause is allowed"
66 )]
67 ConflictingEngineClause {
68 with_engine: String,
69 suffix_engine: String,
70 },
71
72 #[error(
78 "WITH RECURSIVE: only UNION / UNION ALL are allowed in the recursive term; \
79 {op} is not permitted"
80 )]
81 InvalidRecursiveSetOp { op: String },
82
83 #[error("WITH RECURSIVE: invalid self-reference to '{cte_name}' in recursive term: {reason}")]
86 InvalidRecursiveSelfRef { cte_name: String, reason: String },
87
88 #[error(
91 "WITH RECURSIVE CTE '{cte_name}': anchor produces {anchor_cols} column(s) \
92 but {declared_cols} were declared"
93 )]
94 RecursiveColumnMismatch {
95 cte_name: String,
96 anchor_cols: usize,
97 declared_cols: usize,
98 },
99
100 #[error(
104 "WITH RECURSIVE CTE '{cte_name}' exceeded max recursion depth {max_depth}; \
105 add a stricter termination condition or raise max_recursion_depth"
106 )]
107 RecursionDepthExceeded { cte_name: String, max_depth: usize },
108
109 #[error(
114 "collection '{name}' was dropped; \
115 restore with `{undrop_hint}` before retention elapses \
116 at {retention_expires_at_ns} ns"
117 )]
118 CollectionDeactivated {
119 name: String,
120 retention_expires_at_ns: u64,
121 undrop_hint: String,
122 },
123}
124
125impl From<crate::catalog::SqlCatalogError> for SqlError {
126 fn from(e: crate::catalog::SqlCatalogError) -> Self {
127 match e {
128 crate::catalog::SqlCatalogError::RetryableSchemaChanged { descriptor } => {
129 Self::RetryableSchemaChanged { descriptor }
130 }
131 crate::catalog::SqlCatalogError::CollectionDeactivated {
132 name,
133 retention_expires_at_ns,
134 } => {
135 let undrop_hint = format!("UNDROP COLLECTION {name}");
136 Self::CollectionDeactivated {
137 name,
138 retention_expires_at_ns,
139 undrop_hint,
140 }
141 }
142 }
143 }
144}
145
146impl From<nodedb_query::expr_parse::ExprParseError> for SqlError {
147 fn from(e: nodedb_query::expr_parse::ExprParseError) -> Self {
148 Self::Parse {
149 detail: e.to_string(),
150 }
151 }
152}
153
154impl From<sqlparser::parser::ParserError> for SqlError {
155 fn from(e: sqlparser::parser::ParserError) -> Self {
156 Self::Parse {
157 detail: e.to_string(),
158 }
159 }
160}
161
162pub type Result<T> = std::result::Result<T, SqlError>;