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