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("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}