Skip to main content

catalog/
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;
16use std::fmt::Debug;
17
18use common_error::ext::{BoxedError, ErrorExt};
19use common_error::status_code::StatusCode;
20use common_macro::stack_trace_debug;
21use common_query::error::datafusion_status_code;
22use datafusion::error::DataFusionError;
23use snafu::{Location, Snafu};
24
25#[derive(Snafu)]
26#[snafu(visibility(pub))]
27#[stack_trace_debug]
28pub enum Error {
29    #[snafu(display("Failed to list catalogs"))]
30    ListCatalogs {
31        #[snafu(implicit)]
32        location: Location,
33        source: BoxedError,
34    },
35
36    #[snafu(display("Failed to list {}'s schemas", catalog))]
37    ListSchemas {
38        #[snafu(implicit)]
39        location: Location,
40        catalog: String,
41        source: BoxedError,
42    },
43
44    #[snafu(display("Failed to list {}.{}'s tables", catalog, schema))]
45    ListTables {
46        #[snafu(implicit)]
47        location: Location,
48        catalog: String,
49        schema: String,
50        source: BoxedError,
51    },
52
53    #[snafu(display("Failed to list nodes in cluster"))]
54    ListNodes {
55        #[snafu(implicit)]
56        location: Location,
57        source: BoxedError,
58    },
59
60    #[snafu(display("Failed to region stats in cluster"))]
61    ListRegionStats {
62        #[snafu(implicit)]
63        location: Location,
64        source: BoxedError,
65    },
66
67    #[snafu(display("Failed to list flow stats"))]
68    ListFlowStats {
69        #[snafu(implicit)]
70        location: Location,
71        source: BoxedError,
72    },
73
74    #[snafu(display("Failed to list flows in catalog {catalog}"))]
75    ListFlows {
76        #[snafu(implicit)]
77        location: Location,
78        catalog: String,
79        source: BoxedError,
80    },
81
82    #[snafu(display("Flow info not found: {flow_name} in catalog {catalog_name}"))]
83    FlowInfoNotFound {
84        flow_name: String,
85        catalog_name: String,
86        #[snafu(implicit)]
87        location: Location,
88    },
89
90    #[snafu(display("Can't convert value to json, input={input}"))]
91    Json {
92        input: String,
93        #[snafu(source)]
94        error: serde_json::error::Error,
95        #[snafu(implicit)]
96        location: Location,
97    },
98
99    #[snafu(display("Failed to get information extension client"))]
100    GetInformationExtension {
101        #[snafu(implicit)]
102        location: Location,
103    },
104
105    #[snafu(display("Failed to list procedures"))]
106    ListProcedures {
107        #[snafu(implicit)]
108        location: Location,
109        source: BoxedError,
110    },
111
112    #[snafu(display("Procedure id not found"))]
113    ProcedureIdNotFound {
114        #[snafu(implicit)]
115        location: Location,
116    },
117
118    #[snafu(display("convert proto data error"))]
119    ConvertProtoData {
120        #[snafu(implicit)]
121        location: Location,
122        source: BoxedError,
123    },
124
125    #[snafu(display("Failed to create table, table info: {}", table_info))]
126    CreateTable {
127        table_info: String,
128        #[snafu(implicit)]
129        location: Location,
130        source: table::error::Error,
131    },
132
133    #[snafu(display("Cannot find catalog by name: {}", catalog_name))]
134    CatalogNotFound {
135        catalog_name: String,
136        #[snafu(implicit)]
137        location: Location,
138    },
139
140    #[snafu(display("Cannot find schema {} in catalog {}", schema, catalog))]
141    SchemaNotFound {
142        catalog: String,
143        schema: String,
144        #[snafu(implicit)]
145        location: Location,
146    },
147
148    #[snafu(display("Table `{}` already exists", table))]
149    TableExists {
150        table: String,
151        #[snafu(implicit)]
152        location: Location,
153    },
154
155    #[snafu(display("Table not found: {}", table))]
156    TableNotExist {
157        table: String,
158        #[snafu(implicit)]
159        location: Location,
160    },
161
162    #[snafu(display("View info not found: {}", name))]
163    ViewInfoNotFound {
164        name: String,
165        #[snafu(implicit)]
166        location: Location,
167    },
168
169    #[snafu(display(
170        "View plan columns changed from: {} to: {}",
171        origin_names,
172        actual_names
173    ))]
174    ViewPlanColumnsChanged {
175        origin_names: String,
176        actual_names: String,
177        #[snafu(implicit)]
178        location: Location,
179    },
180
181    #[snafu(display("Partition manager not found, it's not expected."))]
182    PartitionManagerNotFound {
183        #[snafu(implicit)]
184        location: Location,
185    },
186
187    #[snafu(display("Failed to find table partitions"))]
188    FindPartitions { source: partition::error::Error },
189
190    #[snafu(display("Failed to find region routes"))]
191    FindRegionRoutes { source: partition::error::Error },
192
193    #[snafu(display("Failed to create recordbatch"))]
194    CreateRecordBatch {
195        #[snafu(implicit)]
196        location: Location,
197        source: common_recordbatch::error::Error,
198    },
199
200    #[snafu(display("Internal error"))]
201    Internal {
202        #[snafu(implicit)]
203        location: Location,
204        source: BoxedError,
205    },
206
207    #[snafu(display("Failed to upgrade weak catalog manager reference"))]
208    UpgradeWeakCatalogManagerRef {
209        #[snafu(implicit)]
210        location: Location,
211    },
212
213    #[snafu(display("Failed to decode logical plan for view: {}", name))]
214    DecodePlan {
215        name: String,
216        #[snafu(implicit)]
217        location: Location,
218        source: common_query::error::Error,
219    },
220
221    #[snafu(display("Illegal access to catalog: {} and schema: {}", catalog, schema))]
222    QueryAccessDenied { catalog: String, schema: String },
223
224    #[snafu(display("DataFusion error"))]
225    Datafusion {
226        #[snafu(source)]
227        error: DataFusionError,
228        #[snafu(implicit)]
229        location: Location,
230    },
231
232    #[snafu(display("Failed to project view columns"))]
233    ProjectViewColumns {
234        #[snafu(source)]
235        error: DataFusionError,
236        #[snafu(implicit)]
237        location: Location,
238    },
239
240    #[snafu(display("Table metadata manager error"))]
241    TableMetadataManager {
242        source: common_meta::error::Error,
243        #[snafu(implicit)]
244        location: Location,
245    },
246
247    #[snafu(display("Failed to get table cache"))]
248    GetTableCache {
249        source: common_meta::error::Error,
250        #[snafu(implicit)]
251        location: Location,
252    },
253
254    #[snafu(display("Failed to get view info from cache"))]
255    GetViewCache {
256        source: common_meta::error::Error,
257        #[snafu(implicit)]
258        location: Location,
259    },
260
261    #[snafu(display("Cache not found: {name}"))]
262    CacheNotFound {
263        name: String,
264        #[snafu(implicit)]
265        location: Location,
266    },
267
268    #[snafu(display("Failed to cast the catalog manager"))]
269    CastManager {
270        #[snafu(implicit)]
271        location: Location,
272    },
273
274    #[snafu(display("Failed to invoke frontend services"))]
275    InvokeFrontend {
276        source: common_frontend::error::Error,
277        #[snafu(implicit)]
278        location: Location,
279    },
280
281    #[snafu(display("Meta client is not provided"))]
282    MetaClientMissing {
283        #[snafu(implicit)]
284        location: Location,
285    },
286
287    #[snafu(display("Failed to find frontend node: {}", addr))]
288    FrontendNotFound {
289        addr: String,
290        #[snafu(implicit)]
291        location: Location,
292    },
293
294    #[snafu(display("Failed to handle query, addr: {}", addr))]
295    HandleQuery {
296        addr: String,
297        source: common_meta::error::Error,
298        #[snafu(implicit)]
299        location: Location,
300    },
301
302    #[snafu(display("Failed to project schema"))]
303    ProjectSchema {
304        source: datatypes::error::Error,
305        #[snafu(implicit)]
306        location: Location,
307    },
308}
309
310impl Error {
311    pub fn should_fail(&self) -> bool {
312        use Error::*;
313
314        matches!(
315            self,
316            GetViewCache { .. }
317                | ViewInfoNotFound { .. }
318                | DecodePlan { .. }
319                | ViewPlanColumnsChanged { .. }
320                | ProjectViewColumns { .. }
321        )
322    }
323}
324
325pub type Result<T> = std::result::Result<T, Error>;
326
327impl ErrorExt for Error {
328    fn status_code(&self) -> StatusCode {
329        match self {
330            Error::SchemaNotFound { .. }
331            | Error::CatalogNotFound { .. }
332            | Error::FindPartitions { .. }
333            | Error::FindRegionRoutes { .. }
334            | Error::CacheNotFound { .. }
335            | Error::CastManager { .. }
336            | Error::Json { .. }
337            | Error::GetInformationExtension { .. }
338            | Error::PartitionManagerNotFound { .. }
339            | Error::ProcedureIdNotFound { .. } => StatusCode::Unexpected,
340
341            Error::ViewPlanColumnsChanged { .. } => StatusCode::InvalidArguments,
342
343            Error::ViewInfoNotFound { .. } => StatusCode::TableNotFound,
344
345            Error::FlowInfoNotFound { .. } => StatusCode::FlowNotFound,
346
347            Error::UpgradeWeakCatalogManagerRef { .. } => StatusCode::Internal,
348
349            Error::CreateRecordBatch { source, .. } => source.status_code(),
350            Error::TableExists { .. } => StatusCode::TableAlreadyExists,
351            Error::TableNotExist { .. } => StatusCode::TableNotFound,
352            Error::ListCatalogs { source, .. }
353            | Error::ListNodes { source, .. }
354            | Error::ListSchemas { source, .. }
355            | Error::ListTables { source, .. }
356            | Error::ListFlows { source, .. }
357            | Error::ListFlowStats { source, .. }
358            | Error::ListProcedures { source, .. }
359            | Error::ListRegionStats { source, .. }
360            | Error::ConvertProtoData { source, .. } => source.status_code(),
361
362            Error::CreateTable { source, .. } => source.status_code(),
363
364            Error::DecodePlan { source, .. } => source.status_code(),
365
366            Error::Internal { source, .. } => source.status_code(),
367
368            Error::QueryAccessDenied { .. } => StatusCode::AccessDenied,
369            Error::Datafusion { error, .. } => datafusion_status_code::<Self>(error, None),
370            Error::ProjectViewColumns { .. } => StatusCode::EngineExecuteQuery,
371            Error::TableMetadataManager { source, .. } => source.status_code(),
372            Error::GetViewCache { source, .. } | Error::GetTableCache { source, .. } => {
373                source.status_code()
374            }
375            Error::InvokeFrontend { source, .. } => source.status_code(),
376            Error::FrontendNotFound { .. } | Error::MetaClientMissing { .. } => {
377                StatusCode::Unexpected
378            }
379            Error::HandleQuery { source, .. } => source.status_code(),
380            Error::ProjectSchema { source, .. } => source.status_code(),
381        }
382    }
383
384    fn as_any(&self) -> &dyn Any {
385        self
386    }
387}
388
389impl From<Error> for DataFusionError {
390    fn from(e: Error) -> Self {
391        DataFusionError::External(Box::new(e))
392    }
393}
394
395#[cfg(test)]
396mod tests {
397    use snafu::GenerateImplicitData;
398
399    use super::*;
400
401    #[test]
402    pub fn test_errors_to_datafusion_error() {
403        let e: DataFusionError = Error::TableExists {
404            table: "test_table".to_string(),
405            location: Location::generate(),
406        }
407        .into();
408        match e {
409            DataFusionError::External(_) => {}
410            _ => {
411                panic!("catalog error should be converted to DataFusionError::Internal")
412            }
413        }
414    }
415}