1use std::any::Any;
16
17use common_error::ext::{ErrorExt, RetryHint};
18use common_error::status_code::StatusCode;
19use common_macro::stack_trace_debug;
20use datafusion::arrow::error::ArrowError;
21use datafusion::error::DataFusionError;
22use object_store::error::retry_hint_from_opendal_error;
23use serde_json::error::Error as JsonError;
24use snafu::{Location, Snafu};
25use store_api::storage::RegionId;
26
27#[derive(Snafu)]
28#[snafu(visibility(pub))]
29#[stack_trace_debug]
30pub enum Error {
31 #[snafu(display("Unsupported operation: {}", operation))]
32 Unsupported {
33 operation: String,
34 #[snafu(implicit)]
35 location: Location,
36 },
37
38 #[snafu(display("Unexpected engine: {}", engine))]
39 UnexpectedEngine {
40 engine: String,
41 #[snafu(implicit)]
42 location: Location,
43 },
44
45 #[snafu(display("Invalid region metadata"))]
46 InvalidMetadata {
47 source: store_api::metadata::MetadataError,
48 #[snafu(implicit)]
49 location: Location,
50 },
51
52 #[snafu(display("Region not found, region_id: {}", region_id))]
53 RegionNotFound {
54 region_id: RegionId,
55 #[snafu(implicit)]
56 location: Location,
57 },
58
59 #[snafu(display("Failed to check object from path: {}", path))]
60 CheckObject {
61 path: String,
62 #[snafu(implicit)]
63 location: Location,
64 #[snafu(source)]
65 error: object_store::Error,
66 },
67
68 #[snafu(display("Fail to encode object into json"))]
69 EncodeJson {
70 #[snafu(implicit)]
71 location: Location,
72 #[snafu(source)]
73 error: JsonError,
74 },
75
76 #[snafu(display("Fail to decode object from json"))]
77 DecodeJson {
78 #[snafu(implicit)]
79 location: Location,
80 #[snafu(source)]
81 error: JsonError,
82 },
83
84 #[snafu(display("Failed to store region manifest, region_id: {}", region_id))]
85 StoreRegionManifest {
86 #[snafu(source)]
87 error: object_store::Error,
88 region_id: RegionId,
89 #[snafu(implicit)]
90 location: Location,
91 },
92
93 #[snafu(display("Failed to load region manifest, region_id: {}", region_id))]
94 LoadRegionManifest {
95 #[snafu(source)]
96 error: object_store::Error,
97 region_id: RegionId,
98 #[snafu(implicit)]
99 location: Location,
100 },
101
102 #[snafu(display("Failed to delete region manifest, region_id: {},", region_id))]
103 DeleteRegionManifest {
104 #[snafu(source)]
105 error: object_store::Error,
106 region_id: RegionId,
107 #[snafu(implicit)]
108 location: Location,
109 },
110
111 #[snafu(display("Manifest already exists: {}", path))]
112 ManifestExists {
113 path: String,
114 #[snafu(implicit)]
115 location: Location,
116 },
117
118 #[snafu(display("Missing required field: {}", name))]
119 MissingRequiredField {
120 name: String,
121 #[snafu(implicit)]
122 location: Location,
123 },
124
125 #[snafu(display("Failed to build backend"))]
126 BuildBackend {
127 #[snafu(implicit)]
128 location: Location,
129 source: common_datasource::error::Error,
130 },
131
132 #[snafu(display("Failed to build stream"))]
133 BuildStream {
134 #[snafu(source)]
135 error: DataFusionError,
136 #[snafu(implicit)]
137 location: Location,
138 },
139
140 #[snafu(display("Failed to project schema"))]
141 ProjectArrowSchema {
142 #[snafu(source)]
143 error: ArrowError,
144 #[snafu(implicit)]
145 location: Location,
146 },
147
148 #[snafu(display("Failed to project schema"))]
149 ProjectSchema {
150 source: datatypes::error::Error,
151 #[snafu(implicit)]
152 location: Location,
153 },
154
155 #[snafu(display("Failed to parse file format"))]
156 ParseFileFormat {
157 #[snafu(implicit)]
158 location: Location,
159 source: common_datasource::error::Error,
160 },
161
162 #[snafu(display("Failed to generate parquet scan plan"))]
163 ParquetScanPlan {
164 #[snafu(source)]
165 error: DataFusionError,
166 #[snafu(implicit)]
167 location: Location,
168 },
169
170 #[snafu(display(
171 "Projection out of bounds, column_index: {}, bounds: {}",
172 column_index,
173 bounds
174 ))]
175 ProjectionOutOfBounds {
176 column_index: usize,
177 bounds: usize,
178 #[snafu(implicit)]
179 location: Location,
180 },
181
182 #[snafu(transparent)]
183 DataFusion {
184 #[snafu(source)]
185 error: DataFusionError,
186 #[snafu(implicit)]
187 location: Location,
188 },
189
190 #[snafu(display("Failed to create default value for column: {}", column))]
191 CreateDefault {
192 column: String,
193 source: datatypes::error::Error,
194 #[snafu(implicit)]
195 location: Location,
196 },
197}
198
199pub type Result<T> = std::result::Result<T, Error>;
200
201impl ErrorExt for Error {
202 fn status_code(&self) -> StatusCode {
203 use Error::*;
204
205 match self {
206 ProjectArrowSchema { .. }
207 | ProjectSchema { .. }
208 | MissingRequiredField { .. }
209 | Unsupported { .. }
210 | InvalidMetadata { .. }
211 | ProjectionOutOfBounds { .. }
212 | CreateDefault { .. } => StatusCode::InvalidArguments,
213
214 RegionNotFound { .. } => StatusCode::RegionNotFound,
215
216 BuildBackend { source, .. } => source.status_code(),
217 ParseFileFormat { source, .. } => source.status_code(),
218
219 CheckObject { .. }
220 | StoreRegionManifest { .. }
221 | LoadRegionManifest { .. }
222 | DeleteRegionManifest { .. } => StatusCode::StorageUnavailable,
223
224 EncodeJson { .. }
225 | DecodeJson { .. }
226 | ManifestExists { .. }
227 | BuildStream { .. }
228 | ParquetScanPlan { .. }
229 | UnexpectedEngine { .. } => StatusCode::Unexpected,
230
231 DataFusion { .. } => StatusCode::Internal,
232 }
233 }
234
235 fn as_any(&self) -> &dyn Any {
236 self
237 }
238
239 fn retry_hint(&self) -> RetryHint {
240 use Error::*;
241
242 match self {
243 BuildBackend { source, .. } | ParseFileFormat { source, .. } => source.retry_hint(),
244 CheckObject { error, .. }
245 | StoreRegionManifest { error, .. }
246 | LoadRegionManifest { error, .. }
247 | DeleteRegionManifest { error, .. } => retry_hint_from_opendal_error(error),
248 _ => RetryHint::NonRetryable,
249 }
250 }
251}