1use 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}