Skip to main content

databend_client/
error.rs

1// Copyright 2021 Datafuse Labs
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 crate::error_code::ErrorCode;
16use reqwest::StatusCode;
17use std::error::Error as StdError;
18
19#[derive(Debug, Clone)]
20pub enum RequestKind {
21    QueryStart,
22    QueryPage,
23    QueryKill,
24    QueryFinal,
25    UploadToStage,
26    StreamingLoad,
27    Login,
28    Heartbeat,
29    SessionRefresh,
30    Other(String),
31}
32
33impl RequestKind {
34    fn as_str(&self) -> &str {
35        match self {
36            Self::QueryStart => "query/start",
37            Self::QueryPage => "query/page",
38            Self::QueryKill => "query/kill",
39            Self::QueryFinal => "query/final",
40            Self::UploadToStage => "upload_to_stage",
41            Self::StreamingLoad => "streaming_load",
42            Self::Login => "login",
43            Self::Heartbeat => "heartbeat",
44            Self::SessionRefresh => "session/refresh",
45            Self::Other(v) => v.as_str(),
46        }
47    }
48}
49
50impl std::fmt::Display for RequestKind {
51    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
52        write!(f, "{}", self.as_str())
53    }
54}
55
56impl From<&str> for RequestKind {
57    fn from(value: &str) -> Self {
58        match value {
59            "query/start" => Self::QueryStart,
60            "query/page" => Self::QueryPage,
61            "query/kill" => Self::QueryKill,
62            "query/final" => Self::QueryFinal,
63            "upload_to_stage" => Self::UploadToStage,
64            "streaming_load" => Self::StreamingLoad,
65            "login" => Self::Login,
66            "heartbeat" => Self::Heartbeat,
67            "session/refresh" => Self::SessionRefresh,
68            other => Self::Other(other.to_string()),
69        }
70    }
71}
72
73impl From<String> for RequestKind {
74    fn from(value: String) -> Self {
75        Self::from(value.as_str())
76    }
77}
78
79#[derive(Debug)]
80pub enum Error {
81    WithContext {
82        inner: Box<Error>,
83        request_kind: Option<RequestKind>,
84        query_id: Option<String>,
85        retry_times: Option<u32>,
86    },
87
88    /// errors detected before sending request.
89    /// e.g. invalid DSN, header value, stage name.
90    BadArgument(String),
91    /// errors when
92    /// 1. accessing local file and presign_url
93    /// 2. From(std::io::Error)
94    IO(String),
95
96    /// send request error
97    Request(String),
98
99    /// http handler return 200, but body is invalid
100    /// 1. failed to decode body to Utf8 or JSON
101    /// 2. failed to decode result data
102    Decode(String),
103
104    /// http handler return 200, but query failed (.error != null)
105    QueryFailed(ErrorCode),
106
107    /// http handler return non-200, with JSON body of type QueryError.
108    Logic(StatusCode, ErrorCode),
109
110    /// other non-200 response
111    Response {
112        status: StatusCode,
113        msg: String,
114    },
115
116    /// the following are more detail type of Logic
117    ///
118    /// possible reasons:
119    ///  - expired: if you have not polled the next_page_uri for too long, the session will be expired, you'll get a 404
120    ///    on accessing this next page uri.
121    ///  - routed to another server
122    ///  - server restarted
123    ///
124    /// TODO: try to distinguish them
125    QueryNotFound(String),
126    AuthFailure(ErrorCode),
127}
128
129impl Error {
130    pub fn response_error(status: StatusCode, body: &[u8]) -> Self {
131        Self::Response {
132            status,
133            msg: String::from_utf8_lossy(body).to_string(),
134        }
135    }
136
137    pub fn with_context(self, request_kind: impl Into<RequestKind>) -> Self {
138        Self::WithContext {
139            inner: Box::new(self),
140            request_kind: Some(request_kind.into()),
141            query_id: None,
142            retry_times: None,
143        }
144    }
145
146    pub fn with_query_id(self, query_id: impl Into<String>) -> Self {
147        match self {
148            Self::WithContext {
149                inner,
150                request_kind,
151                retry_times,
152                ..
153            } => Self::WithContext {
154                inner,
155                request_kind,
156                query_id: Some(query_id.into()),
157                retry_times,
158            },
159            other => Self::WithContext {
160                inner: Box::new(other),
161                request_kind: None,
162                query_id: Some(query_id.into()),
163                retry_times: None,
164            },
165        }
166    }
167
168    pub fn with_retry_times(self, retry_times: u32) -> Self {
169        match self {
170            Self::WithContext {
171                inner,
172                request_kind,
173                query_id,
174                ..
175            } => Self::WithContext {
176                inner,
177                request_kind,
178                query_id,
179                retry_times: Some(retry_times),
180            },
181            other => Self::WithContext {
182                inner: Box::new(other),
183                request_kind: None,
184                query_id: None,
185                retry_times: Some(retry_times),
186            },
187        }
188    }
189
190    pub fn status_code(&self) -> Option<StatusCode> {
191        match self {
192            Self::Logic(status, ..) => Some(*status),
193            Self::Response { status, .. } => Some(*status),
194            Self::WithContext { inner, .. } => inner.status_code(),
195            _ => None,
196        }
197    }
198}
199
200impl std::fmt::Display for Error {
201    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
202        match self {
203            Self::Decode(msg) => write!(f, "DecodeError: {msg}"),
204            Self::BadArgument(msg) => write!(f, "BadArgument: {msg}"),
205            Self::Request(msg) => write!(f, "{msg}"),
206            Self::Response { msg, status } => write!(f, "ResponseError: ({status}){msg}"),
207            Self::IO(msg) => write!(f, "IOError: {msg}"),
208            Self::Logic(status_code, ec) => write!(f, "BadRequest:({status_code}){ec}"),
209            Self::QueryNotFound(msg) => write!(f, "QueryNotFound: {msg}"),
210            Self::QueryFailed(ec) => write!(f, "QueryFailed: {ec}"),
211            Self::AuthFailure(ec) => write!(f, "AuthFailure: {ec}"),
212            Self::WithContext {
213                inner,
214                request_kind,
215                query_id,
216                retry_times,
217            } => {
218                write!(f, "[")?;
219                if let Some(v) = request_kind {
220                    write!(f, "request_kind={v}")?;
221                }
222                if let Some(v) = query_id {
223                    write!(f, " query_id={v}")?;
224                }
225                if let Some(v) = retry_times {
226                    if *v > 1 {
227                        write!(f, " retry_times={v}")?;
228                    }
229                }
230                write!(f, "]: {inner}")
231            }
232        }
233    }
234}
235
236impl std::error::Error for Error {}
237
238pub type Result<T, E = Error> = core::result::Result<T, E>;
239
240impl From<url::ParseError> for Error {
241    fn from(e: url::ParseError) -> Self {
242        Error::Decode(e.to_string())
243    }
244}
245
246impl From<std::num::ParseIntError> for Error {
247    fn from(e: std::num::ParseIntError) -> Self {
248        Error::Decode(e.to_string())
249    }
250}
251
252/// only used in make_headers
253impl From<reqwest::header::InvalidHeaderValue> for Error {
254    fn from(e: reqwest::header::InvalidHeaderValue) -> Self {
255        Error::BadArgument(e.to_string())
256    }
257}
258
259impl From<serde_json::Error> for Error {
260    fn from(e: serde_json::Error) -> Self {
261        Error::Decode(e.to_string())
262    }
263}
264
265impl From<reqwest::Error> for Error {
266    fn from(e: reqwest::Error) -> Self {
267        let e = e.without_url();
268        let source = e
269            .source()
270            .map(|s| format!(", source={}", s))
271            .unwrap_or_default();
272        Error::Request(format!("reqwest::Error: {}{}", e, source))
273    }
274}
275
276impl From<std::io::Error> for Error {
277    fn from(e: std::io::Error) -> Self {
278        Error::IO(e.to_string())
279    }
280}
281
282impl From<std::str::Utf8Error> for Error {
283    fn from(e: std::str::Utf8Error) -> Self {
284        Error::Decode(e.to_string())
285    }
286}