Skip to main content

cli/data/export_v2/
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_error::ext::{ErrorExt, RetryHint};
18use common_error::status_code::StatusCode;
19use common_macro::stack_trace_debug;
20use object_store::error::retry_hint_from_opendal_error;
21use snafu::{Location, Snafu};
22
23#[derive(Snafu)]
24#[snafu(visibility(pub))]
25#[stack_trace_debug]
26pub enum Error {
27    #[snafu(display(
28        "Experimental Metric export requires Parquet and a frontend that explicitly reports experimental_metric_export=true. Use an upgraded, consistently configured frontend entry point."
29    ))]
30    MetricExportUnavailable {
31        #[snafu(implicit)]
32        location: Location,
33    },
34
35    #[snafu(display("Invalid URI '{}': {}", uri, reason))]
36    InvalidUri {
37        uri: String,
38        reason: String,
39        #[snafu(implicit)]
40        location: Location,
41    },
42
43    #[snafu(display("Unsupported storage scheme: {}", scheme))]
44    UnsupportedScheme {
45        scheme: String,
46        #[snafu(implicit)]
47        location: Location,
48    },
49
50    #[snafu(display("Storage operation '{}' failed", operation))]
51    StorageOperation {
52        operation: String,
53        #[snafu(source)]
54        error: object_store::Error,
55        #[snafu(implicit)]
56        location: Location,
57    },
58
59    #[snafu(display("Failed to parse manifest"))]
60    ManifestParse {
61        #[snafu(source)]
62        error: serde_json::Error,
63        #[snafu(implicit)]
64        location: Location,
65    },
66
67    #[snafu(display("Failed to serialize manifest"))]
68    ManifestSerialize {
69        #[snafu(source)]
70        error: serde_json::Error,
71        #[snafu(implicit)]
72        location: Location,
73    },
74
75    #[snafu(display("Failed to decode text file as UTF-8"))]
76    TextDecode {
77        #[snafu(source)]
78        error: std::string::FromUtf8Error,
79        #[snafu(implicit)]
80        location: Location,
81    },
82
83    #[snafu(display("I/O error while {}: {}", operation, error))]
84    Io {
85        operation: &'static str,
86        error: std::io::Error,
87        #[snafu(implicit)]
88        location: Location,
89    },
90
91    #[snafu(display(
92        "Cannot resume snapshot with a different schema_only mode (existing: {}, requested: {}). Use --force to recreate.",
93        existing_schema_only,
94        requested_schema_only
95    ))]
96    SchemaOnlyModeMismatch {
97        existing_schema_only: bool,
98        requested_schema_only: bool,
99        #[snafu(implicit)]
100        location: Location,
101    },
102
103    #[snafu(display(
104        "Cannot resume snapshot with different {} (existing: {}, requested: {}). Use --force to recreate.",
105        field,
106        existing,
107        requested
108    ))]
109    ResumeConfigMismatch {
110        field: String,
111        existing: String,
112        requested: String,
113        #[snafu(implicit)]
114        location: Location,
115    },
116
117    #[snafu(display("Failed to parse time: invalid format: {}", input))]
118    TimeParseInvalidFormat {
119        input: String,
120        #[snafu(implicit)]
121        location: Location,
122    },
123
124    #[snafu(display("Failed to parse time: end_time is before start_time"))]
125    TimeParseEndBeforeStart {
126        #[snafu(implicit)]
127        location: Location,
128    },
129
130    #[snafu(display(
131        "chunk_time_window requires both --start-time and --end-time to be specified"
132    ))]
133    ChunkTimeWindowRequiresBounds {
134        #[snafu(implicit)]
135        location: Location,
136    },
137
138    #[snafu(display("--schema-only cannot be used with data export arguments: {}", args))]
139    SchemaOnlyArgsNotAllowed {
140        args: String,
141        #[snafu(implicit)]
142        location: Location,
143    },
144
145    #[snafu(display("Empty result from query"))]
146    EmptyResult {
147        #[snafu(implicit)]
148        location: Location,
149    },
150
151    #[snafu(display("Unexpected value type in query result"))]
152    UnexpectedValueType {
153        #[snafu(implicit)]
154        location: Location,
155    },
156
157    #[snafu(display("Database error"))]
158    Database {
159        #[snafu(source)]
160        error: crate::error::Error,
161        #[snafu(implicit)]
162        location: Location,
163    },
164
165    #[snafu(display("Snapshot not found at '{}'", uri))]
166    SnapshotNotFound {
167        uri: String,
168        #[snafu(implicit)]
169        location: Location,
170    },
171
172    #[snafu(display("Schema '{}' not found in catalog '{}'", schema, catalog))]
173    SchemaNotFound {
174        catalog: String,
175        schema: String,
176        #[snafu(implicit)]
177        location: Location,
178    },
179
180    #[snafu(display("Failed to parse URL"))]
181    UrlParse {
182        #[snafu(source)]
183        error: url::ParseError,
184        #[snafu(implicit)]
185        location: Location,
186    },
187
188    #[snafu(display("Failed to build object store"))]
189    BuildObjectStore {
190        #[snafu(source)]
191        error: object_store::Error,
192        #[snafu(implicit)]
193        location: Location,
194    },
195
196    #[snafu(display("Manifest version mismatch: expected {}, found {}", expected, found))]
197    ManifestVersionMismatch {
198        expected: u32,
199        found: u32,
200        #[snafu(implicit)]
201        location: Location,
202    },
203
204    #[snafu(display(
205        "Snapshot verification failed: {} error(s), {} warning(s)",
206        errors,
207        warnings
208    ))]
209    SnapshotVerifyFailed {
210        errors: usize,
211        warnings: usize,
212        #[snafu(implicit)]
213        location: Location,
214    },
215}
216
217pub type Result<T> = std::result::Result<T, Error>;
218
219impl ErrorExt for Error {
220    fn status_code(&self) -> StatusCode {
221        match self {
222            Error::MetricExportUnavailable { .. }
223            | Error::InvalidUri { .. }
224            | Error::UnsupportedScheme { .. }
225            | Error::SchemaOnlyModeMismatch { .. }
226            | Error::ResumeConfigMismatch { .. }
227            | Error::ManifestVersionMismatch { .. }
228            | Error::SchemaOnlyArgsNotAllowed { .. }
229            | Error::SnapshotVerifyFailed { .. } => StatusCode::InvalidArguments,
230            Error::TimeParseInvalidFormat { .. }
231            | Error::TimeParseEndBeforeStart { .. }
232            | Error::ChunkTimeWindowRequiresBounds { .. } => StatusCode::InvalidArguments,
233
234            Error::StorageOperation { .. }
235            | Error::ManifestParse { .. }
236            | Error::ManifestSerialize { .. }
237            | Error::TextDecode { .. }
238            | Error::BuildObjectStore { .. } => StatusCode::StorageUnavailable,
239
240            Error::EmptyResult { .. }
241            | Error::UnexpectedValueType { .. }
242            | Error::UrlParse { .. } => StatusCode::Internal,
243
244            Error::Io { .. } => StatusCode::External,
245
246            Error::Database { error, .. } => error.status_code(),
247
248            Error::SnapshotNotFound { .. } => StatusCode::InvalidArguments,
249            Error::SchemaNotFound { .. } => StatusCode::DatabaseNotFound,
250        }
251    }
252
253    fn as_any(&self) -> &dyn Any {
254        self
255    }
256
257    fn retry_hint(&self) -> RetryHint {
258        match self {
259            Error::StorageOperation { error, .. } | Error::BuildObjectStore { error, .. } => {
260                retry_hint_from_opendal_error(error)
261            }
262            Error::Database { error, .. } => error.retry_hint(),
263            _ => RetryHint::NonRetryable,
264        }
265    }
266}