Skip to main content

common_query/
error.rs

1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use 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
283/// Try to get the proper [`StatusCode`] of [`DataFusionError].
284pub 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}