1use std::any::Any;
16use std::fmt::Debug;
17
18use common_error::ext::{BoxedError, ErrorExt};
19use common_error::status_code::StatusCode;
20use common_macro::stack_trace_debug;
21use common_query::error::datafusion_status_code;
22use datafusion::error::DataFusionError;
23use snafu::{Location, Snafu};
24
25#[derive(Snafu)]
26#[snafu(visibility(pub))]
27#[stack_trace_debug]
28pub enum Error {
29 #[snafu(display("Failed to list catalogs"))]
30 ListCatalogs {
31 #[snafu(implicit)]
32 location: Location,
33 source: BoxedError,
34 },
35
36 #[snafu(display("Failed to list {}'s schemas", catalog))]
37 ListSchemas {
38 #[snafu(implicit)]
39 location: Location,
40 catalog: String,
41 source: BoxedError,
42 },
43
44 #[snafu(display("Failed to list {}.{}'s tables", catalog, schema))]
45 ListTables {
46 #[snafu(implicit)]
47 location: Location,
48 catalog: String,
49 schema: String,
50 source: BoxedError,
51 },
52
53 #[snafu(display("Failed to list nodes in cluster"))]
54 ListNodes {
55 #[snafu(implicit)]
56 location: Location,
57 source: BoxedError,
58 },
59
60 #[snafu(display("Failed to region stats in cluster"))]
61 ListRegionStats {
62 #[snafu(implicit)]
63 location: Location,
64 source: BoxedError,
65 },
66
67 #[snafu(display("Failed to list flow stats"))]
68 ListFlowStats {
69 #[snafu(implicit)]
70 location: Location,
71 source: BoxedError,
72 },
73
74 #[snafu(display("Failed to list flows in catalog {catalog}"))]
75 ListFlows {
76 #[snafu(implicit)]
77 location: Location,
78 catalog: String,
79 source: BoxedError,
80 },
81
82 #[snafu(display("Flow info not found: {flow_name} in catalog {catalog_name}"))]
83 FlowInfoNotFound {
84 flow_name: String,
85 catalog_name: String,
86 #[snafu(implicit)]
87 location: Location,
88 },
89
90 #[snafu(display("Can't convert value to json, input={input}"))]
91 Json {
92 input: String,
93 #[snafu(source)]
94 error: serde_json::error::Error,
95 #[snafu(implicit)]
96 location: Location,
97 },
98
99 #[snafu(display("Failed to get information extension client"))]
100 GetInformationExtension {
101 #[snafu(implicit)]
102 location: Location,
103 },
104
105 #[snafu(display("Failed to list procedures"))]
106 ListProcedures {
107 #[snafu(implicit)]
108 location: Location,
109 source: BoxedError,
110 },
111
112 #[snafu(display("Procedure id not found"))]
113 ProcedureIdNotFound {
114 #[snafu(implicit)]
115 location: Location,
116 },
117
118 #[snafu(display("convert proto data error"))]
119 ConvertProtoData {
120 #[snafu(implicit)]
121 location: Location,
122 source: BoxedError,
123 },
124
125 #[snafu(display("Failed to create table, table info: {}", table_info))]
126 CreateTable {
127 table_info: String,
128 #[snafu(implicit)]
129 location: Location,
130 source: table::error::Error,
131 },
132
133 #[snafu(display("Cannot find catalog by name: {}", catalog_name))]
134 CatalogNotFound {
135 catalog_name: String,
136 #[snafu(implicit)]
137 location: Location,
138 },
139
140 #[snafu(display("Cannot find schema {} in catalog {}", schema, catalog))]
141 SchemaNotFound {
142 catalog: String,
143 schema: String,
144 #[snafu(implicit)]
145 location: Location,
146 },
147
148 #[snafu(display("Table `{}` already exists", table))]
149 TableExists {
150 table: String,
151 #[snafu(implicit)]
152 location: Location,
153 },
154
155 #[snafu(display("Table not found: {}", table))]
156 TableNotExist {
157 table: String,
158 #[snafu(implicit)]
159 location: Location,
160 },
161
162 #[snafu(display("View info not found: {}", name))]
163 ViewInfoNotFound {
164 name: String,
165 #[snafu(implicit)]
166 location: Location,
167 },
168
169 #[snafu(display(
170 "View plan columns changed from: {} to: {}",
171 origin_names,
172 actual_names
173 ))]
174 ViewPlanColumnsChanged {
175 origin_names: String,
176 actual_names: String,
177 #[snafu(implicit)]
178 location: Location,
179 },
180
181 #[snafu(display("Partition manager not found, it's not expected."))]
182 PartitionManagerNotFound {
183 #[snafu(implicit)]
184 location: Location,
185 },
186
187 #[snafu(display("Failed to find table partitions"))]
188 FindPartitions { source: partition::error::Error },
189
190 #[snafu(display("Failed to find region routes"))]
191 FindRegionRoutes { source: partition::error::Error },
192
193 #[snafu(display("Failed to create recordbatch"))]
194 CreateRecordBatch {
195 #[snafu(implicit)]
196 location: Location,
197 source: common_recordbatch::error::Error,
198 },
199
200 #[snafu(display("Internal error"))]
201 Internal {
202 #[snafu(implicit)]
203 location: Location,
204 source: BoxedError,
205 },
206
207 #[snafu(display("Failed to upgrade weak catalog manager reference"))]
208 UpgradeWeakCatalogManagerRef {
209 #[snafu(implicit)]
210 location: Location,
211 },
212
213 #[snafu(display("Failed to decode logical plan for view: {}", name))]
214 DecodePlan {
215 name: String,
216 #[snafu(implicit)]
217 location: Location,
218 source: common_query::error::Error,
219 },
220
221 #[snafu(display("Invalid table info in catalog"))]
222 InvalidTableInfoInCatalog {
223 #[snafu(implicit)]
224 location: Location,
225 source: datatypes::error::Error,
226 },
227
228 #[snafu(display("Illegal access to catalog: {} and schema: {}", catalog, schema))]
229 QueryAccessDenied { catalog: String, schema: String },
230
231 #[snafu(display("DataFusion error"))]
232 Datafusion {
233 #[snafu(source)]
234 error: DataFusionError,
235 #[snafu(implicit)]
236 location: Location,
237 },
238
239 #[snafu(display("Failed to project view columns"))]
240 ProjectViewColumns {
241 #[snafu(source)]
242 error: DataFusionError,
243 #[snafu(implicit)]
244 location: Location,
245 },
246
247 #[snafu(display("Table metadata manager error"))]
248 TableMetadataManager {
249 source: common_meta::error::Error,
250 #[snafu(implicit)]
251 location: Location,
252 },
253
254 #[snafu(display("Failed to get table cache"))]
255 GetTableCache {
256 source: common_meta::error::Error,
257 #[snafu(implicit)]
258 location: Location,
259 },
260
261 #[snafu(display("Failed to get view info from cache"))]
262 GetViewCache {
263 source: common_meta::error::Error,
264 #[snafu(implicit)]
265 location: Location,
266 },
267
268 #[snafu(display("Cache not found: {name}"))]
269 CacheNotFound {
270 name: String,
271 #[snafu(implicit)]
272 location: Location,
273 },
274
275 #[snafu(display("Failed to cast the catalog manager"))]
276 CastManager {
277 #[snafu(implicit)]
278 location: Location,
279 },
280
281 #[snafu(display("Failed to invoke frontend services"))]
282 InvokeFrontend {
283 source: common_frontend::error::Error,
284 #[snafu(implicit)]
285 location: Location,
286 },
287
288 #[snafu(display("Meta client is not provided"))]
289 MetaClientMissing {
290 #[snafu(implicit)]
291 location: Location,
292 },
293
294 #[snafu(display("Failed to find frontend node: {}", addr))]
295 FrontendNotFound {
296 addr: String,
297 #[snafu(implicit)]
298 location: Location,
299 },
300}
301
302impl Error {
303 pub fn should_fail(&self) -> bool {
304 use Error::*;
305
306 matches!(
307 self,
308 GetViewCache { .. }
309 | ViewInfoNotFound { .. }
310 | DecodePlan { .. }
311 | ViewPlanColumnsChanged { .. }
312 | ProjectViewColumns { .. }
313 )
314 }
315}
316
317pub type Result<T> = std::result::Result<T, Error>;
318
319impl ErrorExt for Error {
320 fn status_code(&self) -> StatusCode {
321 match self {
322 Error::SchemaNotFound { .. }
323 | Error::CatalogNotFound { .. }
324 | Error::FindPartitions { .. }
325 | Error::FindRegionRoutes { .. }
326 | Error::CacheNotFound { .. }
327 | Error::CastManager { .. }
328 | Error::Json { .. }
329 | Error::GetInformationExtension { .. }
330 | Error::PartitionManagerNotFound { .. }
331 | Error::ProcedureIdNotFound { .. } => StatusCode::Unexpected,
332
333 Error::ViewPlanColumnsChanged { .. } => StatusCode::InvalidArguments,
334
335 Error::ViewInfoNotFound { .. } => StatusCode::TableNotFound,
336
337 Error::FlowInfoNotFound { .. } => StatusCode::FlowNotFound,
338
339 Error::UpgradeWeakCatalogManagerRef { .. } => StatusCode::Internal,
340
341 Error::CreateRecordBatch { source, .. } => source.status_code(),
342 Error::TableExists { .. } => StatusCode::TableAlreadyExists,
343 Error::TableNotExist { .. } => StatusCode::TableNotFound,
344 Error::ListCatalogs { source, .. }
345 | Error::ListNodes { source, .. }
346 | Error::ListSchemas { source, .. }
347 | Error::ListTables { source, .. }
348 | Error::ListFlows { source, .. }
349 | Error::ListFlowStats { source, .. }
350 | Error::ListProcedures { source, .. }
351 | Error::ListRegionStats { source, .. }
352 | Error::ConvertProtoData { source, .. } => source.status_code(),
353
354 Error::CreateTable { source, .. } => source.status_code(),
355
356 Error::DecodePlan { source, .. } => source.status_code(),
357 Error::InvalidTableInfoInCatalog { source, .. } => source.status_code(),
358
359 Error::Internal { source, .. } => source.status_code(),
360
361 Error::QueryAccessDenied { .. } => StatusCode::AccessDenied,
362 Error::Datafusion { error, .. } => datafusion_status_code::<Self>(error, None),
363 Error::ProjectViewColumns { .. } => StatusCode::EngineExecuteQuery,
364 Error::TableMetadataManager { source, .. } => source.status_code(),
365 Error::GetViewCache { source, .. } | Error::GetTableCache { source, .. } => {
366 source.status_code()
367 }
368 Error::InvokeFrontend { source, .. } => source.status_code(),
369 Error::FrontendNotFound { .. } | Error::MetaClientMissing { .. } => {
370 StatusCode::Unexpected
371 }
372 }
373 }
374
375 fn as_any(&self) -> &dyn Any {
376 self
377 }
378}
379
380impl From<Error> for DataFusionError {
381 fn from(e: Error) -> Self {
382 DataFusionError::External(Box::new(e))
383 }
384}
385
386#[cfg(test)]
387mod tests {
388 use snafu::GenerateImplicitData;
389
390 use super::*;
391
392 #[test]
393 pub fn test_errors_to_datafusion_error() {
394 let e: DataFusionError = Error::TableExists {
395 table: "test_table".to_string(),
396 location: Location::generate(),
397 }
398 .into();
399 match e {
400 DataFusionError::External(_) => {}
401 _ => {
402 panic!("catalog error should be converted to DataFusionError::Internal")
403 }
404 }
405 }
406}