Skip to main content

common_datasource/
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_schema::ArrowError;
18use common_error::ext::{ErrorExt, RetryHint, retry_hint_from_io_error};
19use common_error::status_code::StatusCode;
20use common_macro::stack_trace_debug;
21use datafusion::parquet::errors::ParquetError;
22use object_store::error::retry_hint_from_opendal_error;
23use snafu::{Location, Snafu};
24use url::ParseError;
25
26#[derive(Snafu)]
27#[snafu(visibility(pub))]
28#[stack_trace_debug]
29pub enum Error {
30    #[snafu(display("Invalid packed snapshot: {reason}"))]
31    InvalidPackedSnapshot { reason: String },
32
33    #[snafu(display("Parquet write cancelled"))]
34    ParquetWriteCancelled {},
35
36    #[snafu(display("Parquet writer limits must be positive"))]
37    InvalidParquetWriterLimits {},
38
39    #[snafu(display("Parquet writer resource limit exceeded: {reason}"))]
40    ParquetWriterResource { reason: String },
41
42    #[snafu(display("Unsupported compression type: {}", compression_type))]
43    UnsupportedCompressionType {
44        compression_type: String,
45        #[snafu(implicit)]
46        location: Location,
47    },
48
49    #[snafu(display("Unsupported backend protocol: {}, url: {}", protocol, url))]
50    UnsupportedBackendProtocol {
51        protocol: String,
52        #[snafu(implicit)]
53        location: Location,
54        url: String,
55    },
56
57    #[snafu(display("Unsupported format protocol: {}", format))]
58    UnsupportedFormat {
59        format: String,
60        #[snafu(implicit)]
61        location: Location,
62    },
63
64    #[snafu(display("empty host: {}", url))]
65    EmptyHostPath {
66        url: String,
67        #[snafu(implicit)]
68        location: Location,
69    },
70
71    #[snafu(display("Invalid url: {}", url))]
72    InvalidUrl {
73        url: String,
74        #[snafu(source)]
75        error: ParseError,
76        #[snafu(implicit)]
77        location: Location,
78    },
79
80    #[snafu(display(
81        "SQL access to the local filesystem is disabled for '{}'; use S3, OSS, GCS, or AzBlob instead",
82        path
83    ))]
84    LocalFileAccessDisabled {
85        path: String,
86        #[snafu(implicit)]
87        location: Location,
88    },
89
90    #[snafu(display(
91        "Local filesystem path '{}' is outside the configured copy root or is unsafe: {}; use a path relative to the copy root or use S3, OSS, GCS, or AzBlob",
92        path,
93        reason
94    ))]
95    LocalFileAccessDenied {
96        path: String,
97        reason: String,
98        #[snafu(implicit)]
99        location: Location,
100    },
101
102    #[snafu(display(
103        "Local filesystem path '{}' does not exist within the configured copy root",
104        path
105    ))]
106    LocalFilePathNotFound {
107        path: String,
108        #[snafu(implicit)]
109        location: Location,
110    },
111
112    #[snafu(display("Location must include a file or object name: '{}'", path))]
113    MissingObjectName {
114        path: String,
115        #[snafu(implicit)]
116        location: Location,
117    },
118
119    #[snafu(display("Invalid local filesystem root '{}'", root))]
120    InvalidLocalFileRoot {
121        root: String,
122        #[snafu(source)]
123        error: std::io::Error,
124        #[snafu(implicit)]
125        location: Location,
126    },
127
128    #[snafu(display("Invalid local filesystem root '{}': {}", root, reason))]
129    InvalidLocalFileRootConfig {
130        root: String,
131        reason: String,
132        #[snafu(implicit)]
133        location: Location,
134    },
135
136    #[snafu(display("Failed to build backend"))]
137    BuildBackend {
138        #[snafu(source)]
139        error: object_store::Error,
140        #[snafu(implicit)]
141        location: Location,
142    },
143
144    #[snafu(display("Failed to build orc reader"))]
145    OrcReader {
146        #[snafu(implicit)]
147        location: Location,
148        #[snafu(source)]
149        error: orc_rust::error::OrcError,
150    },
151
152    #[snafu(display("Failed to read object from path: {}", path))]
153    ReadObject {
154        path: String,
155        #[snafu(implicit)]
156        location: Location,
157        #[snafu(source)]
158        error: object_store::Error,
159    },
160
161    #[snafu(display("Failed to write object to path: {}", path))]
162    WriteObject {
163        path: String,
164        #[snafu(implicit)]
165        location: Location,
166        #[snafu(source)]
167        error: object_store::Error,
168    },
169
170    #[snafu(display("Failed to write"))]
171    AsyncWrite {
172        #[snafu(source)]
173        error: std::io::Error,
174        #[snafu(implicit)]
175        location: Location,
176    },
177
178    #[snafu(display("Failed to write record batch"))]
179    WriteRecordBatch {
180        #[snafu(implicit)]
181        location: Location,
182        #[snafu(source)]
183        error: ArrowError,
184    },
185
186    #[snafu(display("Failed to encode record batch"))]
187    EncodeRecordBatch {
188        #[snafu(implicit)]
189        location: Location,
190        #[snafu(source)]
191        error: ParquetError,
192    },
193
194    #[snafu(display("Failed to read record batch"))]
195    ReadRecordBatch {
196        #[snafu(implicit)]
197        location: Location,
198        #[snafu(source)]
199        error: datafusion::error::DataFusionError,
200    },
201
202    #[snafu(display("Failed to read parquet"))]
203    ReadParquetSnafu {
204        #[snafu(implicit)]
205        location: Location,
206        #[snafu(source)]
207        error: datafusion::parquet::errors::ParquetError,
208    },
209
210    #[snafu(display("Failed to convert parquet to schema"))]
211    ParquetToSchema {
212        #[snafu(implicit)]
213        location: Location,
214        #[snafu(source)]
215        error: datafusion::parquet::errors::ParquetError,
216    },
217
218    #[snafu(display("Failed to infer schema from file"))]
219    InferSchema {
220        #[snafu(implicit)]
221        location: Location,
222        #[snafu(source)]
223        error: arrow_schema::ArrowError,
224    },
225
226    #[snafu(display("Failed to list object in path: {}", path))]
227    ListObjects {
228        path: String,
229        #[snafu(implicit)]
230        location: Location,
231        #[snafu(source)]
232        error: object_store::Error,
233    },
234
235    #[snafu(display("Invalid connection: {}", msg))]
236    InvalidConnection {
237        msg: String,
238        #[snafu(implicit)]
239        location: Location,
240    },
241
242    #[snafu(display("Failed to join handle"))]
243    JoinHandle {
244        #[snafu(implicit)]
245        location: Location,
246        #[snafu(source)]
247        error: tokio::task::JoinError,
248    },
249
250    #[snafu(display("Failed to parse format {} with value: {}", key, value))]
251    ParseFormat {
252        key: String,
253        value: String,
254        #[snafu(implicit)]
255        location: Location,
256    },
257
258    #[snafu(display("Failed to merge schema"))]
259    MergeSchema {
260        #[snafu(source)]
261        error: arrow_schema::ArrowError,
262        #[snafu(implicit)]
263        location: Location,
264    },
265
266    #[snafu(display("Failed to write parquet file, path: {}", path))]
267    WriteParquet {
268        path: String,
269        #[snafu(implicit)]
270        location: Location,
271        #[snafu(source)]
272        error: parquet::errors::ParquetError,
273    },
274
275    #[snafu(transparent)]
276    DataFusion {
277        #[snafu(implicit)]
278        location: Location,
279        #[snafu(source)]
280        error: datafusion::error::DataFusionError,
281    },
282}
283
284pub type Result<T> = std::result::Result<T, Error>;
285
286impl ErrorExt for Error {
287    fn status_code(&self) -> StatusCode {
288        use Error::*;
289        match self {
290            ParquetWriteCancelled {} => StatusCode::Cancelled,
291            InvalidPackedSnapshot { .. } => StatusCode::InvalidArguments,
292            InvalidParquetWriterLimits {} => StatusCode::InvalidArguments,
293            ParquetWriterResource { .. } => StatusCode::Suspended,
294            BuildBackend { .. }
295            | ListObjects { .. }
296            | ReadObject { .. }
297            | WriteObject { .. }
298            | AsyncWrite { .. }
299            | WriteParquet { .. } => StatusCode::StorageUnavailable,
300
301            UnsupportedBackendProtocol { .. }
302            | UnsupportedCompressionType { .. }
303            | UnsupportedFormat { .. }
304            | InvalidConnection { .. }
305            | InvalidUrl { .. }
306            | LocalFileAccessDisabled { .. }
307            | LocalFileAccessDenied { .. }
308            | LocalFilePathNotFound { .. }
309            | MissingObjectName { .. }
310            | InvalidLocalFileRoot { .. }
311            | InvalidLocalFileRootConfig { .. }
312            | EmptyHostPath { .. }
313            | InferSchema { .. }
314            | ReadParquetSnafu { .. }
315            | ParquetToSchema { .. }
316            | ParseFormat { .. }
317            | MergeSchema { .. } => StatusCode::InvalidArguments,
318
319            JoinHandle { .. }
320            | ReadRecordBatch { .. }
321            | WriteRecordBatch { .. }
322            | EncodeRecordBatch { .. }
323            | OrcReader { .. } => StatusCode::Unexpected,
324
325            DataFusion { .. } => StatusCode::Internal,
326        }
327    }
328
329    fn as_any(&self) -> &dyn Any {
330        self
331    }
332
333    fn retry_hint(&self) -> RetryHint {
334        use Error::*;
335
336        match self {
337            BuildBackend { error, .. }
338            | ListObjects { error, .. }
339            | ReadObject { error, .. }
340            | WriteObject { error, .. } => retry_hint_from_opendal_error(error),
341            AsyncWrite { error, .. } => retry_hint_from_io_error(error),
342            WriteParquet { .. } => RetryHint::Retryable,
343            _ => RetryHint::NonRetryable,
344        }
345    }
346}