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("Failed to collect recordbatch"))]
178    CollectRecordbatch {
179        #[snafu(implicit)]
180        location: Location,
181        source: common_recordbatch::error::Error,
182    },
183
184    #[snafu(display("Failed to plan statement"))]
185    PlanStatement {
186        #[snafu(implicit)]
187        location: Location,
188        source: query::error::Error,
189    },
190
191    #[snafu(display("Failed to read table: {table_name}"))]
192    ReadTable {
193        table_name: String,
194        #[snafu(implicit)]
195        location: Location,
196        source: query::error::Error,
197    },
198
199    #[snafu(display("Failed to execute logical plan"))]
200    ExecLogicalPlan {
201        #[snafu(implicit)]
202        location: Location,
203        source: query::error::Error,
204    },
205
206    #[snafu(display("Not supported: {}", feat))]
207    NotSupported { feat: String },
208
209    #[snafu(display("SQL execution intercepted"))]
210    SqlExecIntercepted {
211        #[snafu(implicit)]
212        location: Location,
213        source: BoxedError,
214    },
215
216    // TODO(ruihang): merge all query execution error kinds
217    #[snafu(display("Failed to execute PromQL query {}", query))]
218    ExecutePromql {
219        query: String,
220        #[snafu(implicit)]
221        location: Location,
222        source: servers::error::Error,
223    },
224
225    #[snafu(display("Failed to create logical plan for prometheus query"))]
226    PromStoreRemoteQueryPlan {
227        #[snafu(implicit)]
228        location: Location,
229        source: servers::error::Error,
230    },
231
232    #[snafu(display("Failed to create logical plan for prometheus metric names query"))]
233    PrometheusMetricNamesQueryPlan {
234        #[snafu(implicit)]
235        location: Location,
236        source: servers::error::Error,
237    },
238
239    #[snafu(display("Failed to create logical plan for prometheus label values query"))]
240    PrometheusLabelValuesQueryPlan {
241        #[snafu(implicit)]
242        location: Location,
243        source: query::promql::error::Error,
244    },
245
246    #[snafu(display("Failed to describe schema for given statement"))]
247    DescribeStatement {
248        #[snafu(implicit)]
249        location: Location,
250        source: query::error::Error,
251    },
252
253    #[snafu(display("Illegal primary keys definition: {}", msg))]
254    IllegalPrimaryKeysDef {
255        msg: String,
256        #[snafu(implicit)]
257        location: Location,
258    },
259
260    #[snafu(display("Failed to insert value into table: {}", table_name))]
261    Insert {
262        table_name: String,
263        #[snafu(implicit)]
264        location: Location,
265        source: table::error::Error,
266    },
267
268    #[snafu(display("Unsupported format: {:?}", format))]
269    UnsupportedFormat {
270        #[snafu(implicit)]
271        location: Location,
272        format: Format,
273    },
274
275    #[snafu(display("Failed to pass permission check"))]
276    Permission {
277        source: auth::error::Error,
278        #[snafu(implicit)]
279        location: Location,
280    },
281
282    #[snafu(display(
283        "No valid default value can be built automatically, column: {}",
284        column,
285    ))]
286    ColumnNoneDefaultValue {
287        column: String,
288        #[snafu(implicit)]
289        location: Location,
290    },
291
292    #[snafu(display("Table operation error"))]
293    TableOperation {
294        source: operator::error::Error,
295        #[snafu(implicit)]
296        location: Location,
297    },
298
299    #[snafu(display("Invalid auth config"))]
300    IllegalAuthConfig { source: auth::error::Error },
301
302    #[snafu(display("Failed to serialize options to TOML"))]
303    TomlFormat {
304        #[snafu(implicit)]
305        location: Location,
306        #[snafu(source(from(common_config::error::Error, Box::new)))]
307        source: Box<common_config::error::Error>,
308    },
309
310    #[snafu(display("Failed to get cache from cache registry: {}", name))]
311    CacheRequired {
312        #[snafu(implicit)]
313        location: Location,
314        name: String,
315    },
316
317    #[snafu(display("Invalid tls config"))]
318    InvalidTlsConfig {
319        #[snafu(source)]
320        error: common_grpc::error::Error,
321        #[snafu(implicit)]
322        location: Location,
323    },
324
325    #[snafu(display("Failed to init plugin"))]
326    // this comment is to bypass the unused snafu check in "check-snafu.py"
327    InitPlugin {
328        #[snafu(implicit)]
329        location: Location,
330        source: BoxedError,
331    },
332
333    #[snafu(display("Failed to decode logical plan from substrait"))]
334    SubstraitDecodeLogicalPlan {
335        #[snafu(implicit)]
336        location: Location,
337        source: common_query::error::Error,
338    },
339
340    #[snafu(display("DataFusionError"))]
341    DataFusion {
342        #[snafu(source)]
343        error: DataFusionError,
344        #[snafu(implicit)]
345        location: Location,
346    },
347
348    #[snafu(display("Query has been cancelled"))]
349    Cancelled {
350        #[snafu(implicit)]
351        location: Location,
352    },
353
354    #[snafu(display("Canceling statement due to statement timeout"))]
355    StatementTimeout {
356        #[snafu(implicit)]
357        location: Location,
358    },
359
360    #[snafu(display("Service suspended"))]
361    Suspended {
362        #[snafu(implicit)]
363        location: Location,
364    },
365}
366
367pub type Result<T> = std::result::Result<T, Error>;
368
369impl ErrorExt for Error {
370    fn status_code(&self) -> StatusCode {
371        match self {
372            Error::TomlFormat { .. }
373            | Error::ParseAddr { .. }
374            | Error::InvalidSql { .. }
375            | Error::InvalidInsertRequest { .. }
376            | Error::InvalidDeleteRequest { .. }
377            | Error::IllegalPrimaryKeysDef { .. }
378            | Error::SchemaExists { .. }
379            | Error::ColumnNotFound { .. }
380            | Error::AmbiguousValueColumn { .. }
381            | Error::UnsupportedFormat { .. }
382            | Error::IllegalAuthConfig { .. }
383            | Error::ColumnNoneDefaultValue { .. }
384            | Error::IncompleteGrpcRequest { .. }
385            | Error::InvalidTlsConfig { .. } => StatusCode::InvalidArguments,
386
387            Error::NotSupported { .. } => StatusCode::Unsupported,
388
389            Error::Permission { source, .. } => source.status_code(),
390
391            Error::DescribeStatement { source, .. } => source.status_code(),
392
393            Error::HandleHeartbeatResponse { source, .. } => source.status_code(),
394
395            Error::PromStoreRemoteQueryPlan { source, .. }
396            | Error::PrometheusMetricNamesQueryPlan { source, .. }
397            | Error::ExecutePromql { source, .. } => source.status_code(),
398
399            Error::SubstraitDecodeLogicalPlan { source, .. } => source.status_code(),
400
401            Error::PrometheusLabelValuesQueryPlan { source, .. } => source.status_code(),
402
403            Error::CollectRecordbatch { source, .. } => source.status_code(),
404
405            Error::SqlExecIntercepted { source, .. } => source.status_code(),
406            Error::StartServer { source, .. } => source.status_code(),
407            Error::ShutdownServer { source, .. } => source.status_code(),
408
409            Error::ParseSql { source, .. } => source.status_code(),
410
411            Error::InvalidateTableCache { source, .. } => source.status_code(),
412
413            Error::Table { source, .. } | Error::Insert { source, .. } => source.status_code(),
414
415            Error::RequestQuery { source, .. } => source.status_code(),
416
417            Error::CacheRequired { .. } => StatusCode::Internal,
418
419            Error::TableNotFound { .. } => StatusCode::TableNotFound,
420
421            Error::Catalog { source, .. } => source.status_code(),
422
423            Error::CreateMetaHeartbeatStream { source, .. } => source.status_code(),
424
425            Error::PlanStatement { source, .. }
426            | Error::ReadTable { source, .. }
427            | Error::ExecLogicalPlan { source, .. } => source.status_code(),
428
429            Error::External { source, .. } | Error::InitPlugin { source, .. } => {
430                source.status_code()
431            }
432            Error::FindRegionPeer { source, .. } => source.status_code(),
433
434            Error::TableOperation { source, .. } => source.status_code(),
435
436            Error::DataFusion { error, .. } => datafusion_status_code::<Self>(error, None),
437
438            Error::Cancelled { .. } => StatusCode::Cancelled,
439
440            Error::StatementTimeout { .. } => StatusCode::Cancelled,
441
442            Error::Suspended { .. } => StatusCode::Suspended,
443        }
444    }
445
446    fn as_any(&self) -> &dyn Any {
447        self
448    }
449
450    fn retry_hint(&self) -> RetryHint {
451        match self {
452            Error::InvalidateTableCache { source, .. }
453            | Error::HandleHeartbeatResponse { source, .. }
454            | Error::RequestQuery { source, .. } => source.retry_hint(),
455
456            Error::External { source, .. }
457            | Error::SqlExecIntercepted { source, .. }
458            | Error::InitPlugin { source, .. } => source.retry_hint(),
459
460            Error::StartServer { source, .. }
461            | Error::ShutdownServer { source, .. }
462            | Error::ExecutePromql { source, .. }
463            | Error::PromStoreRemoteQueryPlan { source, .. }
464            | Error::PrometheusMetricNamesQueryPlan { source, .. } => source.retry_hint(),
465
466            Error::ParseSql { source, .. } => source.retry_hint(),
467            Error::Catalog { source, .. } => source.retry_hint(),
468            Error::CreateMetaHeartbeatStream { source, .. } => source.retry_hint(),
469            Error::FindRegionPeer { source, .. } => source.retry_hint(),
470            Error::Table { source, .. } => source.retry_hint(),
471            Error::CollectRecordbatch { source, .. } => source.retry_hint(),
472            Error::PlanStatement { source, .. }
473            | Error::ReadTable { source, .. }
474            | Error::ExecLogicalPlan { source, .. }
475            | Error::DescribeStatement { source, .. } => source.retry_hint(),
476            Error::PrometheusLabelValuesQueryPlan { source, .. } => source.retry_hint(),
477            Error::Insert { source, .. } => source.retry_hint(),
478            Error::Permission { source, .. } => source.retry_hint(),
479            Error::TableOperation { source, .. } => source.retry_hint(),
480            Error::IllegalAuthConfig { source, .. } => source.retry_hint(),
481            Error::TomlFormat { source, .. } => source.retry_hint(),
482            Error::InvalidTlsConfig { error, .. } => error.retry_hint(),
483            Error::SubstraitDecodeLogicalPlan { source, .. } => source.retry_hint(),
484
485            _ => RetryHint::NonRetryable,
486        }
487    }
488}
489
490define_into_tonic_status!(Error);
491
492impl From<operator::error::Error> for Error {
493    fn from(e: operator::error::Error) -> Error {
494        Error::TableOperation {
495            source: e,
496            location: Location::default(),
497        }
498    }
499}