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