Skip to main content

frontend/
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 common_datasource::file_format::Format;
18use common_error::define_into_tonic_status;
19use common_error::ext::{BoxedError, ErrorExt, RetryHint};
20use common_error::status_code::StatusCode;
21use common_macro::stack_trace_debug;
22use common_query::error::datafusion_status_code;
23use datafusion::error::DataFusionError;
24use session::ReadPreference;
25use snafu::{Location, Snafu};
26use store_api::storage::RegionId;
27
28#[derive(Snafu)]
29#[snafu(visibility(pub))]
30#[stack_trace_debug]
31pub enum Error {
32    #[snafu(display("Failed to invalidate table cache"))]
33    InvalidateTableCache {
34        #[snafu(implicit)]
35        location: Location,
36        source: common_meta::error::Error,
37    },
38
39    #[snafu(display("Failed to handle heartbeat response"))]
40    HandleHeartbeatResponse {
41        #[snafu(implicit)]
42        location: Location,
43        source: common_meta::error::Error,
44    },
45
46    #[snafu(display("External error"))]
47    External {
48        #[snafu(implicit)]
49        location: Location,
50        source: BoxedError,
51    },
52
53    #[snafu(display("Failed to query"))]
54    RequestQuery {
55        #[snafu(implicit)]
56        location: Location,
57        source: common_meta::error::Error,
58    },
59
60    #[snafu(display("Failed to start server"))]
61    StartServer {
62        #[snafu(implicit)]
63        location: Location,
64        source: servers::error::Error,
65    },
66
67    #[snafu(display("Failed to shutdown server"))]
68    ShutdownServer {
69        #[snafu(implicit)]
70        location: Location,
71        source: servers::error::Error,
72    },
73
74    #[snafu(display("Failed to parse address {}", addr))]
75    ParseAddr {
76        addr: String,
77        #[snafu(source)]
78        error: std::net::AddrParseError,
79    },
80
81    #[snafu(display("Failed to parse SQL"))]
82    ParseSql {
83        #[snafu(implicit)]
84        location: Location,
85        source: sql::error::Error,
86    },
87
88    #[snafu(display("Invalid SQL, error: {}", err_msg))]
89    InvalidSql {
90        err_msg: String,
91        #[snafu(implicit)]
92        location: Location,
93    },
94
95    #[snafu(display("Incomplete GRPC request: {}", err_msg))]
96    IncompleteGrpcRequest {
97        err_msg: String,
98        #[snafu(implicit)]
99        location: Location,
100    },
101
102    #[snafu(display("Invalid InsertRequest, reason: {}", reason))]
103    InvalidInsertRequest {
104        reason: String,
105        #[snafu(implicit)]
106        location: Location,
107    },
108
109    #[snafu(display("Invalid DeleteRequest, reason: {}", reason))]
110    InvalidDeleteRequest {
111        reason: String,
112        #[snafu(implicit)]
113        location: Location,
114    },
115
116    #[snafu(display("Table not found: {}", table_name))]
117    TableNotFound { table_name: String },
118
119    #[snafu(display("General catalog error"))]
120    Catalog {
121        #[snafu(implicit)]
122        location: Location,
123        source: catalog::error::Error,
124    },
125
126    #[snafu(display("Failed to create heartbeat stream to Metasrv"))]
127    CreateMetaHeartbeatStream {
128        source: meta_client::error::Error,
129        #[snafu(implicit)]
130        location: Location,
131    },
132
133    #[snafu(display(
134        "Failed to find region peer for region id {}, read preference: {}",
135        region_id,
136        read_preference
137    ))]
138    FindRegionPeer {
139        region_id: RegionId,
140        read_preference: ReadPreference,
141        #[snafu(implicit)]
142        location: Location,
143        source: partition::error::Error,
144    },
145
146    #[snafu(display("Schema {} already exists", name))]
147    SchemaExists {
148        name: String,
149        #[snafu(implicit)]
150        location: Location,
151    },
152
153    #[snafu(display("Table occurs error"))]
154    Table {
155        #[snafu(implicit)]
156        location: Location,
157        source: table::error::Error,
158    },
159
160    #[snafu(display("Cannot find column by name: {}", msg))]
161    ColumnNotFound {
162        msg: String,
163        #[snafu(implicit)]
164        location: Location,
165    },
166
167    #[snafu(display(
168        "Ambiguous value column in table '{table_name}', candidates: {field_columns:?}"
169    ))]
170    AmbiguousValueColumn {
171        table_name: String,
172        field_columns: Vec<String>,
173        #[snafu(implicit)]
174        location: Location,
175    },
176
177    #[snafu(display("Unexpected type {data_type} for column '{column}' of table '{table_name}'"))]
178    UnexpectedColumnType {
179        table_name: String,
180        column: String,
181        data_type: String,
182        #[snafu(implicit)]
183        location: Location,
184    },
185
186    #[snafu(display("Failed to collect recordbatch"))]
187    CollectRecordbatch {
188        #[snafu(implicit)]
189        location: Location,
190        source: common_recordbatch::error::Error,
191    },
192
193    #[snafu(display("Failed to plan statement"))]
194    PlanStatement {
195        #[snafu(implicit)]
196        location: Location,
197        source: query::error::Error,
198    },
199
200    #[snafu(display("Failed to read table: {table_name}"))]
201    ReadTable {
202        table_name: String,
203        #[snafu(implicit)]
204        location: Location,
205        source: query::error::Error,
206    },
207
208    #[snafu(display("Failed to execute logical plan"))]
209    ExecLogicalPlan {
210        #[snafu(implicit)]
211        location: Location,
212        source: query::error::Error,
213    },
214
215    #[snafu(display("Not supported: {}", feat))]
216    NotSupported { feat: String },
217
218    #[snafu(display("SQL execution intercepted"))]
219    SqlExecIntercepted {
220        #[snafu(implicit)]
221        location: Location,
222        source: BoxedError,
223    },
224
225    // TODO(ruihang): merge all query execution error kinds
226    #[snafu(display("Failed to execute PromQL query {}", query))]
227    ExecutePromql {
228        query: String,
229        #[snafu(implicit)]
230        location: Location,
231        source: servers::error::Error,
232    },
233
234    #[snafu(display("Invalid Prometheus remote read schema: {detail}"))]
235    InvalidPromRemoteReadSchema {
236        detail: String,
237        #[snafu(implicit)]
238        location: Location,
239    },
240
241    #[snafu(display("Failed to create logical plan for prometheus query"))]
242    PromStoreRemoteQueryPlan {
243        #[snafu(implicit)]
244        location: Location,
245        source: servers::error::Error,
246    },
247
248    #[snafu(display("Failed to create logical plan for prometheus metric names query"))]
249    PrometheusMetricNamesQueryPlan {
250        #[snafu(implicit)]
251        location: Location,
252        source: servers::error::Error,
253    },
254
255    #[snafu(display("Failed to create logical plan for prometheus label values query"))]
256    PrometheusLabelValuesQueryPlan {
257        #[snafu(implicit)]
258        location: Location,
259        source: query::promql::error::Error,
260    },
261
262    #[snafu(display("Failed to describe schema for given statement"))]
263    DescribeStatement {
264        #[snafu(implicit)]
265        location: Location,
266        source: query::error::Error,
267    },
268
269    #[snafu(display("Illegal primary keys definition: {}", msg))]
270    IllegalPrimaryKeysDef {
271        msg: String,
272        #[snafu(implicit)]
273        location: Location,
274    },
275
276    #[snafu(display("Failed to insert value into table: {}", table_name))]
277    Insert {
278        table_name: String,
279        #[snafu(implicit)]
280        location: Location,
281        source: table::error::Error,
282    },
283
284    #[snafu(display("Unsupported format: {:?}", format))]
285    UnsupportedFormat {
286        #[snafu(implicit)]
287        location: Location,
288        format: Format,
289    },
290
291    #[snafu(display("Failed to pass permission check"))]
292    Permission {
293        source: auth::error::Error,
294        #[snafu(implicit)]
295        location: Location,
296    },
297
298    #[snafu(display(
299        "No valid default value can be built automatically, column: {}",
300        column,
301    ))]
302    ColumnNoneDefaultValue {
303        column: String,
304        #[snafu(implicit)]
305        location: Location,
306    },
307
308    #[snafu(display("Table operation error"))]
309    TableOperation {
310        source: operator::error::Error,
311        #[snafu(implicit)]
312        location: Location,
313    },
314
315    #[snafu(display("Invalid auth config"))]
316    IllegalAuthConfig { source: auth::error::Error },
317
318    #[snafu(display("Failed to serialize options to TOML"))]
319    TomlFormat {
320        #[snafu(implicit)]
321        location: Location,
322        #[snafu(source(from(common_config::error::Error, Box::new)))]
323        source: Box<common_config::error::Error>,
324    },
325
326    #[snafu(display("Failed to get cache from cache registry: {}", name))]
327    CacheRequired {
328        #[snafu(implicit)]
329        location: Location,
330        name: String,
331    },
332
333    #[snafu(display("Invalid write retry config: {}", reason))]
334    InvalidWriteRetryConfig {
335        reason: &'static str,
336        #[snafu(implicit)]
337        location: Location,
338    },
339
340    #[snafu(display("Invalid tls config"))]
341    InvalidTlsConfig {
342        #[snafu(source)]
343        error: common_grpc::error::Error,
344        #[snafu(implicit)]
345        location: Location,
346    },
347
348    #[snafu(display("Failed to init plugin"))]
349    // this comment is to bypass the unused snafu check in "check-snafu.py"
350    InitPlugin {
351        #[snafu(implicit)]
352        location: Location,
353        source: BoxedError,
354    },
355
356    #[snafu(display("Failed to decode logical plan from substrait"))]
357    SubstraitDecodeLogicalPlan {
358        #[snafu(implicit)]
359        location: Location,
360        source: common_query::error::Error,
361    },
362
363    #[snafu(display("DataFusionError"))]
364    DataFusion {
365        #[snafu(source)]
366        error: DataFusionError,
367        #[snafu(implicit)]
368        location: Location,
369    },
370
371    #[snafu(display("Query has been cancelled"))]
372    Cancelled {
373        #[snafu(implicit)]
374        location: Location,
375    },
376
377    #[snafu(display("Canceling statement due to statement timeout"))]
378    StatementTimeout {
379        #[snafu(implicit)]
380        location: Location,
381    },
382
383    #[snafu(display("Service suspended"))]
384    Suspended {
385        #[snafu(implicit)]
386        location: Location,
387    },
388}
389
390pub type Result<T> = std::result::Result<T, Error>;
391
392impl ErrorExt for Error {
393    fn status_code(&self) -> StatusCode {
394        match self {
395            Error::TomlFormat { .. }
396            | Error::ParseAddr { .. }
397            | Error::InvalidSql { .. }
398            | Error::InvalidInsertRequest { .. }
399            | Error::InvalidDeleteRequest { .. }
400            | Error::IllegalPrimaryKeysDef { .. }
401            | Error::SchemaExists { .. }
402            | Error::ColumnNotFound { .. }
403            | Error::AmbiguousValueColumn { .. }
404            | Error::InvalidPromRemoteReadSchema { .. }
405            | Error::UnsupportedFormat { .. }
406            | Error::IllegalAuthConfig { .. }
407            | Error::ColumnNoneDefaultValue { .. }
408            | Error::IncompleteGrpcRequest { .. }
409            | Error::InvalidTlsConfig { .. }
410            | Error::InvalidWriteRetryConfig { .. } => StatusCode::InvalidArguments,
411
412            Error::NotSupported { .. } => StatusCode::Unsupported,
413
414            Error::Permission { source, .. } => source.status_code(),
415
416            Error::DescribeStatement { source, .. } => source.status_code(),
417
418            Error::HandleHeartbeatResponse { source, .. } => source.status_code(),
419
420            Error::PromStoreRemoteQueryPlan { source, .. }
421            | Error::PrometheusMetricNamesQueryPlan { source, .. }
422            | Error::ExecutePromql { source, .. } => source.status_code(),
423
424            Error::SubstraitDecodeLogicalPlan { source, .. } => source.status_code(),
425
426            Error::PrometheusLabelValuesQueryPlan { source, .. } => source.status_code(),
427
428            Error::CollectRecordbatch { source, .. } => source.status_code(),
429
430            Error::SqlExecIntercepted { source, .. } => source.status_code(),
431            Error::StartServer { source, .. } => source.status_code(),
432            Error::ShutdownServer { source, .. } => source.status_code(),
433
434            Error::ParseSql { source, .. } => source.status_code(),
435
436            Error::InvalidateTableCache { source, .. } => source.status_code(),
437
438            Error::Table { source, .. } | Error::Insert { source, .. } => source.status_code(),
439
440            Error::RequestQuery { source, .. } => source.status_code(),
441
442            Error::CacheRequired { .. } | Error::UnexpectedColumnType { .. } => {
443                StatusCode::Internal
444            }
445
446            Error::TableNotFound { .. } => StatusCode::TableNotFound,
447
448            Error::Catalog { source, .. } => source.status_code(),
449
450            Error::CreateMetaHeartbeatStream { source, .. } => source.status_code(),
451
452            Error::PlanStatement { source, .. }
453            | Error::ReadTable { source, .. }
454            | Error::ExecLogicalPlan { source, .. } => source.status_code(),
455
456            Error::External { source, .. } | Error::InitPlugin { source, .. } => {
457                source.status_code()
458            }
459            Error::FindRegionPeer { source, .. } => source.status_code(),
460
461            Error::TableOperation { source, .. } => source.status_code(),
462
463            Error::DataFusion { error, .. } => datafusion_status_code::<Self>(error, None),
464
465            Error::Cancelled { .. } => StatusCode::Cancelled,
466
467            Error::StatementTimeout { .. } => StatusCode::Cancelled,
468
469            Error::Suspended { .. } => StatusCode::Suspended,
470        }
471    }
472
473    fn as_any(&self) -> &dyn Any {
474        self
475    }
476
477    fn retry_hint(&self) -> RetryHint {
478        match self {
479            Error::InvalidateTableCache { source, .. }
480            | Error::HandleHeartbeatResponse { source, .. }
481            | Error::RequestQuery { source, .. } => source.retry_hint(),
482
483            Error::External { source, .. }
484            | Error::SqlExecIntercepted { source, .. }
485            | Error::InitPlugin { source, .. } => source.retry_hint(),
486
487            Error::StartServer { source, .. }
488            | Error::ShutdownServer { source, .. }
489            | Error::ExecutePromql { source, .. }
490            | Error::PromStoreRemoteQueryPlan { source, .. }
491            | Error::PrometheusMetricNamesQueryPlan { source, .. } => source.retry_hint(),
492
493            Error::ParseSql { source, .. } => source.retry_hint(),
494            Error::Catalog { source, .. } => source.retry_hint(),
495            Error::CreateMetaHeartbeatStream { source, .. } => source.retry_hint(),
496            Error::FindRegionPeer { source, .. } => source.retry_hint(),
497            Error::Table { source, .. } => source.retry_hint(),
498            Error::CollectRecordbatch { source, .. } => source.retry_hint(),
499            Error::PlanStatement { source, .. }
500            | Error::ReadTable { source, .. }
501            | Error::ExecLogicalPlan { source, .. }
502            | Error::DescribeStatement { source, .. } => source.retry_hint(),
503            Error::PrometheusLabelValuesQueryPlan { source, .. } => source.retry_hint(),
504            Error::Insert { source, .. } => source.retry_hint(),
505            Error::Permission { source, .. } => source.retry_hint(),
506            Error::TableOperation { source, .. } => source.retry_hint(),
507            Error::IllegalAuthConfig { source, .. } => source.retry_hint(),
508            Error::TomlFormat { source, .. } => source.retry_hint(),
509            Error::InvalidTlsConfig { error, .. } => error.retry_hint(),
510            Error::SubstraitDecodeLogicalPlan { source, .. } => source.retry_hint(),
511
512            _ => RetryHint::NonRetryable,
513        }
514    }
515}
516
517define_into_tonic_status!(Error);
518
519impl From<operator::error::Error> for Error {
520    fn from(e: operator::error::Error) -> Error {
521        Error::TableOperation {
522            source: e,
523            location: Location::default(),
524        }
525    }
526}