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