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