1use std::any::Any;
16use std::sync::Arc;
17
18use arrow::error::ArrowError;
19use common_error::ext::{BoxedError, ErrorExt, RetryHint};
20use common_error::status_code::StatusCode;
21use common_macro::stack_trace_debug;
22use datafusion_common::DataFusionError;
23use datatypes::arrow;
24use datatypes::arrow::datatypes::DataType as ArrowDatatype;
25use datatypes::error::Error as DataTypeError;
26use datatypes::prelude::ConcreteDataType;
27use snafu::{Location, Snafu};
28
29#[derive(Snafu)]
30#[snafu(visibility(pub))]
31#[stack_trace_debug]
32pub enum Error {
33 #[snafu(display("Storage write did not complete: {source}"))]
34 StorageWrite {
35 source: Arc<BoxedError>,
36 #[snafu(implicit)]
37 location: Location,
38 },
39 #[snafu(display("Unsupported input datatypes {:?} in function {}", datatypes, function))]
40 UnsupportedInputDataType {
41 function: String,
42 datatypes: Vec<ConcreteDataType>,
43 #[snafu(implicit)]
44 location: Location,
45 },
46
47 #[snafu(display("Failed to cast scalar value into vector"))]
48 FromScalarValue {
49 #[snafu(implicit)]
50 location: Location,
51 source: DataTypeError,
52 },
53
54 #[snafu(display("Failed to cast arrow array into vector: {:?}", data_type))]
55 IntoVector {
56 #[snafu(implicit)]
57 location: Location,
58 source: DataTypeError,
59 data_type: ArrowDatatype,
60 },
61
62 #[snafu(display("Invalid input type: {}", err_msg))]
63 InvalidInputType {
64 #[snafu(implicit)]
65 location: Location,
66 source: DataTypeError,
67 err_msg: String,
68 },
69
70 #[snafu(display(
71 "Illegal input_types status, check if DataFusion has changed its UDAF execution logic"
72 ))]
73 InvalidInputState {
74 #[snafu(implicit)]
75 location: Location,
76 },
77
78 #[snafu(transparent)]
79 GeneralDataFusion {
80 #[snafu(source)]
81 error: DataFusionError,
82 #[snafu(implicit)]
83 location: Location,
84 },
85
86 #[snafu(display("Failed to convert DataFusion's recordbatch stream"))]
87 ConvertDfRecordBatchStream {
88 #[snafu(implicit)]
89 location: Location,
90 source: common_recordbatch::error::Error,
91 },
92
93 #[snafu(display("Failed to cast array to {:?}", typ))]
94 TypeCast {
95 #[snafu(source)]
96 error: ArrowError,
97 typ: arrow::datatypes::DataType,
98 #[snafu(implicit)]
99 location: Location,
100 },
101
102 #[snafu(display("Failed to perform compute operation on arrow arrays"))]
103 ArrowCompute {
104 #[snafu(source)]
105 error: ArrowError,
106 #[snafu(implicit)]
107 location: Location,
108 },
109
110 #[snafu(display("Query engine fail to cast value"))]
111 ToScalarValue {
112 #[snafu(implicit)]
113 location: Location,
114 source: DataTypeError,
115 },
116
117 #[snafu(display("Failed to get scalar vector"))]
118 GetScalarVector {
119 #[snafu(implicit)]
120 location: Location,
121 source: DataTypeError,
122 },
123
124 #[snafu(display("Failed to execute function: {source}"))]
125 Execute {
126 #[snafu(implicit)]
127 location: Location,
128 source: BoxedError,
129 },
130
131 #[snafu(display("Failed to decode logical plan: {source}"))]
132 DecodePlan {
133 #[snafu(implicit)]
134 location: Location,
135 source: BoxedError,
136 },
137
138 #[snafu(display("Failed to do table mutation"))]
139 TableMutation {
140 source: BoxedError,
141 #[snafu(implicit)]
142 location: Location,
143 },
144
145 #[snafu(display("Failed to do procedure task"))]
146 ProcedureService {
147 source: BoxedError,
148 #[snafu(implicit)]
149 location: Location,
150 },
151
152 #[snafu(display("Missing TableMutationHandler, not expected"))]
153 MissingTableMutationHandler {
154 #[snafu(implicit)]
155 location: Location,
156 },
157
158 #[snafu(display("Missing ProcedureServiceHandler, not expected"))]
159 MissingProcedureServiceHandler {
160 #[snafu(implicit)]
161 location: Location,
162 },
163
164 #[snafu(display("Missing FlowServiceHandler, not expected"))]
165 MissingFlowServiceHandler {
166 #[snafu(implicit)]
167 location: Location,
168 },
169
170 #[snafu(display("Invalid function args: {}", err_msg))]
171 InvalidFuncArgs {
172 err_msg: String,
173 #[snafu(implicit)]
174 location: Location,
175 },
176
177 #[snafu(display("Permission denied: {}", err_msg))]
178 PermissionDenied {
179 err_msg: String,
180 #[snafu(implicit)]
181 location: Location,
182 },
183
184 #[snafu(display("Can't found alive flownode"))]
185 FlownodeNotFound {
186 #[snafu(implicit)]
187 location: Location,
188 },
189
190 #[snafu(display("Invalid vector string: {}", vec_str))]
191 InvalidVectorString {
192 vec_str: String,
193 source: DataTypeError,
194 #[snafu(implicit)]
195 location: Location,
196 },
197
198 #[snafu(display("Failed to register UDF: {}", name))]
199 RegisterUdf {
200 name: String,
201 #[snafu(source)]
202 error: DataFusionError,
203 #[snafu(implicit)]
204 location: Location,
205 },
206
207 #[snafu(display("Invalid character in prefix config: {}", prefix))]
208 InvalidColumnPrefix { prefix: String },
209
210 #[snafu(display(
211 "DynFilterPayload::Datafusion is {} bytes, which exceeds the configured limit of {} bytes",
212 payload_size_bytes,
213 max_payload_bytes
214 ))]
215 DynFilterPayloadTooLarge {
216 payload_size_bytes: usize,
217 max_payload_bytes: usize,
218 #[snafu(implicit)]
219 location: Location,
220 },
221}
222
223pub type Result<T> = std::result::Result<T, Error>;
224
225impl ErrorExt for Error {
226 fn status_code(&self) -> StatusCode {
227 match self {
228 Error::StorageWrite { source, .. } => source.status_code(),
229 Error::InvalidInputState { .. }
230 | Error::ToScalarValue { .. }
231 | Error::GetScalarVector { .. }
232 | Error::ArrowCompute { .. }
233 | Error::FlownodeNotFound { .. } => StatusCode::EngineExecuteQuery,
234
235 Error::GeneralDataFusion { error, .. } => datafusion_status_code::<Self>(error, None),
236
237 Error::InvalidInputType { source, .. }
238 | Error::IntoVector { source, .. }
239 | Error::FromScalarValue { source, .. }
240 | Error::InvalidVectorString { source, .. } => source.status_code(),
241
242 Error::MissingTableMutationHandler { .. }
243 | Error::MissingProcedureServiceHandler { .. }
244 | Error::MissingFlowServiceHandler { .. }
245 | Error::RegisterUdf { .. } => StatusCode::Unexpected,
246
247 Error::UnsupportedInputDataType { .. }
248 | Error::TypeCast { .. }
249 | Error::InvalidFuncArgs { .. }
250 | Error::InvalidColumnPrefix { .. } => StatusCode::InvalidArguments,
251
252 Error::DynFilterPayloadTooLarge { .. } => StatusCode::PlanQuery,
253
254 Error::ConvertDfRecordBatchStream { source, .. } => source.status_code(),
255
256 Error::DecodePlan { source, .. }
257 | Error::Execute { source, .. }
258 | Error::ProcedureService { source, .. }
259 | Error::TableMutation { source, .. } => source.status_code(),
260
261 Error::PermissionDenied { .. } => StatusCode::PermissionDenied,
262 }
263 }
264
265 fn as_any(&self) -> &dyn Any {
266 self
267 }
268
269 fn retry_hint(&self) -> RetryHint {
270 match self {
271 Error::StorageWrite { source, .. } => source.retry_hint(),
272 _ => RetryHint::NonRetryable,
273 }
274 }
275}
276
277impl From<Error> for DataFusionError {
278 fn from(e: Error) -> DataFusionError {
279 DataFusionError::External(Box::new(e))
280 }
281}
282
283pub fn datafusion_status_code<T: ErrorExt + 'static>(
285 e: &DataFusionError,
286 default_status: Option<StatusCode>,
287) -> StatusCode {
288 let mut error = e;
289 loop {
290 error = match error {
291 DataFusionError::Shared(inner) => inner,
292 DataFusionError::Context(_, inner) | DataFusionError::Diagnostic(_, inner) => inner,
293 _ => break,
294 };
295 }
296
297 match error {
298 DataFusionError::Internal(_) => StatusCode::Internal,
299 DataFusionError::NotImplemented(_) => StatusCode::Unsupported,
300 DataFusionError::Plan(_) => StatusCode::PlanQuery,
301 DataFusionError::External(error) => {
302 if let Some(ext) = (*error).downcast_ref::<T>() {
303 ext.status_code()
304 } else if let Some(ext) = (*error).downcast_ref::<BoxedError>() {
305 ext.status_code()
306 } else {
307 default_status.unwrap_or(StatusCode::EngineExecuteQuery)
308 }
309 }
310 _ => default_status.unwrap_or(StatusCode::EngineExecuteQuery),
311 }
312}
313
314#[cfg(test)]
315mod tests {
316 use std::sync::Arc;
317
318 use common_error::ext::PlainError;
319 use common_error::mock::MockError;
320
321 use super::*;
322
323 #[tokio::test]
324 async fn test_write_completion_preserves_retry_hint() {
325 for hint in [
326 RetryHint::NonRetryable,
327 RetryHint::Retryable,
328 RetryHint::RetryAfterRouteRefresh,
329 ] {
330 let source =
331 || BoxedError::new(MockError::new(StatusCode::RegionBusy).with_retry_hint(hint));
332 let progress = crate::WriteProgress::failed(source());
333 let error = progress.clone().into_result().unwrap_err();
334 assert_eq!(error.status_code(), StatusCode::RegionBusy);
335 assert_eq!(error.retry_hint(), hint);
336 assert_eq!(progress.error.unwrap().retry_hint(), hint);
337
338 let error = crate::WriteCompletion::new(std::future::ready(Err(source())))
339 .wait()
340 .await
341 .unwrap_err();
342 assert_eq!(error.status_code(), StatusCode::RegionBusy);
343 assert_eq!(error.retry_hint(), hint);
344 }
345 }
346
347 #[test]
348 fn test_datafusion_status_code_preserves_external_errors_through_wrappers() {
349 let boxed_error = |status| {
350 DataFusionError::External(Box::new(BoxedError::new(PlainError::new(
351 "neutral error".to_string(),
352 status,
353 ))))
354 };
355
356 for status in [
357 StatusCode::RequestOutdated,
358 StatusCode::Unknown,
359 StatusCode::Unsupported,
360 ] {
361 let errors = [
362 boxed_error(status),
363 DataFusionError::Shared(Arc::new(boxed_error(status))),
364 DataFusionError::Context("context".to_string(), Box::new(boxed_error(status))),
365 DataFusionError::Diagnostic(
366 Box::new(datafusion_common::Diagnostic::new_error("diagnostic", None)),
367 Box::new(boxed_error(status)),
368 ),
369 DataFusionError::Shared(Arc::new(DataFusionError::Context(
370 "context".to_string(),
371 Box::new(DataFusionError::Diagnostic(
372 Box::new(datafusion_common::Diagnostic::new_error("diagnostic", None)),
373 Box::new(boxed_error(status)),
374 )),
375 ))),
376 ];
377
378 for error in errors {
379 assert_eq!(datafusion_status_code::<Error>(&error, None), status);
380 assert_eq!(
381 datafusion_status_code::<Error>(&error, Some(StatusCode::PlanQuery)),
382 status
383 );
384 }
385 }
386
387 let direct_error = DataFusionError::External(Box::new(Error::DynFilterPayloadTooLarge {
388 payload_size_bytes: 2,
389 max_payload_bytes: 1,
390 location: Location::default(),
391 }));
392 assert_eq!(
393 datafusion_status_code::<Error>(&direct_error, Some(StatusCode::Internal)),
394 StatusCode::PlanQuery
395 );
396
397 let boundary_error =
398 DataFusionError::Shared(Arc::new(DataFusionError::External(Box::new(
399 BoxedError::new(common_recordbatch::error::Error::PhysicalExpr {
400 error: DataFusionError::NotImplemented("inner error".to_string()),
401 location: Location::default(),
402 }),
403 ))));
404 assert_eq!(
405 datafusion_status_code::<Error>(&boundary_error, Some(StatusCode::PlanQuery)),
406 StatusCode::Internal
407 );
408 }
409
410 #[test]
411 fn test_datafusion_status_code_uses_default_for_untyped_errors() {
412 let wrap = |error| {
413 DataFusionError::Shared(Arc::new(DataFusionError::Context(
414 "context".to_string(),
415 Box::new(DataFusionError::Diagnostic(
416 Box::new(datafusion_common::Diagnostic::new_error("diagnostic", None)),
417 Box::new(error),
418 )),
419 )))
420 };
421 let errors = || {
422 [
423 (
424 DataFusionError::External(Box::new(std::io::Error::other("neutral io error"))),
425 StatusCode::EngineExecuteQuery,
426 StatusCode::Unknown,
427 ),
428 (
429 DataFusionError::Internal("neutral internal error".to_string()),
430 StatusCode::Internal,
431 StatusCode::Internal,
432 ),
433 (
434 DataFusionError::NotImplemented("neutral not implemented error".to_string()),
435 StatusCode::Unsupported,
436 StatusCode::Unsupported,
437 ),
438 (
439 DataFusionError::Plan("neutral plan error".to_string()),
440 StatusCode::PlanQuery,
441 StatusCode::PlanQuery,
442 ),
443 (
444 DataFusionError::External(Box::new(DataFusionError::Internal(
445 "inner error".to_string(),
446 ))),
447 StatusCode::EngineExecuteQuery,
448 StatusCode::Unknown,
449 ),
450 ]
451 };
452
453 for (error, none_expected, default_expected) in
454 errors()
455 .into_iter()
456 .chain(errors().map(|(error, none_expected, default_expected)| {
457 (wrap(error), none_expected, default_expected)
458 }))
459 {
460 assert_eq!(datafusion_status_code::<Error>(&error, None), none_expected);
461 assert_eq!(
462 datafusion_status_code::<Error>(&error, Some(StatusCode::Unknown)),
463 default_expected
464 );
465 }
466 }
467}