1 | // Copyright (c) 2011 The LevelDB Authors. All rights reserved. |
2 | // Use of this source code is governed by a BSD-style license that can be |
3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. |
4 | // |
5 | // A Status encapsulates the result of an operation. It may indicate success, |
6 | // or it may indicate an error with an associated error message. |
7 | // |
8 | // Multiple threads can invoke const methods on a Status without |
9 | // external synchronization, but if any of the threads may call a |
10 | // non-const method, all threads accessing the same Status must use |
11 | // external synchronization. |
12 | |
13 | // Adapted from Apache Kudu, TensorFlow |
14 | |
15 | #ifndef ARROW_STATUS_H_ |
16 | #define ARROW_STATUS_H_ |
17 | |
18 | #include <cstring> |
19 | #include <iosfwd> |
20 | #include <string> |
21 | #include <utility> |
22 | |
23 | #ifdef ARROW_EXTRA_ERROR_CONTEXT |
24 | #include <sstream> |
25 | #endif |
26 | |
27 | #include "arrow/util/macros.h" |
28 | #include "arrow/util/string_builder.h" |
29 | #include "arrow/util/visibility.h" |
30 | |
31 | #ifdef ARROW_EXTRA_ERROR_CONTEXT |
32 | |
33 | /// \brief Return with given status if condition is met. |
34 | #define ARROW_RETURN_IF_(condition, status, expr) \ |
35 | do { \ |
36 | if (ARROW_PREDICT_FALSE(condition)) { \ |
37 | ::arrow::Status _s = (status); \ |
38 | std::stringstream ss; \ |
39 | ss << _s.message() << "\n" << __FILE__ << ":" << __LINE__ << " code: " << expr; \ |
40 | return ::arrow::Status(_s.code(), ss.str()); \ |
41 | } \ |
42 | } while (0) |
43 | |
44 | #else |
45 | |
46 | #define ARROW_RETURN_IF_(condition, status, _) \ |
47 | do { \ |
48 | if (ARROW_PREDICT_FALSE(condition)) { \ |
49 | return (status); \ |
50 | } \ |
51 | } while (0) |
52 | |
53 | #endif // ARROW_EXTRA_ERROR_CONTEXT |
54 | |
55 | #define ARROW_RETURN_IF(condition, status) \ |
56 | ARROW_RETURN_IF_(condition, status, ARROW_STRINGIFY(status)) |
57 | |
58 | /// \brief Propagate any non-successful Status to the caller |
59 | #define ARROW_RETURN_NOT_OK(status) \ |
60 | do { \ |
61 | ::arrow::Status __s = (status); \ |
62 | ARROW_RETURN_IF_(!__s.ok(), __s, ARROW_STRINGIFY(status)); \ |
63 | } while (false) |
64 | |
65 | #define RETURN_NOT_OK_ELSE(s, else_) \ |
66 | do { \ |
67 | ::arrow::Status _s = (s); \ |
68 | if (!_s.ok()) { \ |
69 | else_; \ |
70 | return _s; \ |
71 | } \ |
72 | } while (false) |
73 | |
74 | // This is an internal-use macro and should not be used in public headers. |
75 | #ifndef RETURN_NOT_OK |
76 | #define RETURN_NOT_OK(s) ARROW_RETURN_NOT_OK(s) |
77 | #endif |
78 | |
79 | namespace arrow { |
80 | |
81 | enum class StatusCode : char { |
82 | OK = 0, |
83 | OutOfMemory = 1, |
84 | KeyError = 2, |
85 | TypeError = 3, |
86 | Invalid = 4, |
87 | IOError = 5, |
88 | CapacityError = 6, |
89 | UnknownError = 9, |
90 | NotImplemented = 10, |
91 | SerializationError = 11, |
92 | PythonError = 12, |
93 | RError = 13, |
94 | PlasmaObjectExists = 20, |
95 | PlasmaObjectNonexistent = 21, |
96 | PlasmaStoreFull = 22, |
97 | PlasmaObjectAlreadySealed = 23, |
98 | StillExecuting = 24, |
99 | // Gandiva range of errors |
100 | CodeGenError = 40, |
101 | ExpressionValidationError = 41, |
102 | ExecutionError = 42 |
103 | }; |
104 | |
105 | #if defined(__clang__) |
106 | // Only clang supports warn_unused_result as a type annotation. |
107 | class ARROW_MUST_USE_RESULT ARROW_EXPORT Status; |
108 | #endif |
109 | |
110 | /// \brief Status outcome object (success or error) |
111 | /// |
112 | /// The Status object is an object holding the outcome of an operation. |
113 | /// The outcome is represented as a StatusCode, either success |
114 | /// (StatusCode::OK) or an error (any other of the StatusCode enumeration values). |
115 | /// |
116 | /// Additionally, if an error occurred, a specific error message is generally |
117 | /// attached. |
118 | class ARROW_EXPORT Status { |
119 | public: |
120 | // Create a success status. |
121 | Status() noexcept : state_(NULLPTR) {} |
122 | ~Status() noexcept { |
123 | // ARROW-2400: On certain compilers, splitting off the slow path improves |
124 | // performance significantly. |
125 | if (ARROW_PREDICT_FALSE(state_ != NULL)) { |
126 | DeleteState(); |
127 | } |
128 | } |
129 | |
130 | Status(StatusCode code, const std::string& msg); |
131 | |
132 | // Copy the specified status. |
133 | inline Status(const Status& s); |
134 | inline Status& operator=(const Status& s); |
135 | |
136 | // Move the specified status. |
137 | inline Status(Status&& s) noexcept; |
138 | inline Status& operator=(Status&& s) noexcept; |
139 | |
140 | // AND the statuses. |
141 | inline Status operator&(const Status& s) const noexcept; |
142 | inline Status operator&(Status&& s) const noexcept; |
143 | inline Status& operator&=(const Status& s) noexcept; |
144 | inline Status& operator&=(Status&& s) noexcept; |
145 | |
146 | /// Return a success status |
147 | static Status OK() { return Status(); } |
148 | |
149 | /// Return a success status with a specific message |
150 | template <typename... Args> |
151 | static Status OK(Args&&... args) { |
152 | return Status(StatusCode::OK, util::StringBuilder(std::forward<Args>(args)...)); |
153 | } |
154 | |
155 | /// Return an error status for out-of-memory conditions |
156 | template <typename... Args> |
157 | static Status OutOfMemory(Args&&... args) { |
158 | return Status(StatusCode::OutOfMemory, |
159 | util::StringBuilder(std::forward<Args>(args)...)); |
160 | } |
161 | |
162 | /// Return an error status for failed key lookups (e.g. column name in a table) |
163 | template <typename... Args> |
164 | static Status KeyError(Args&&... args) { |
165 | return Status(StatusCode::KeyError, util::StringBuilder(std::forward<Args>(args)...)); |
166 | } |
167 | |
168 | /// Return an error status for type errors (such as mismatching data types) |
169 | template <typename... Args> |
170 | static Status TypeError(Args&&... args) { |
171 | return Status(StatusCode::TypeError, |
172 | util::StringBuilder(std::forward<Args>(args)...)); |
173 | } |
174 | |
175 | /// Return an error status for unknown errors |
176 | template <typename... Args> |
177 | static Status UnknownError(Args&&... args) { |
178 | return Status(StatusCode::UnknownError, |
179 | util::StringBuilder(std::forward<Args>(args)...)); |
180 | } |
181 | |
182 | /// Return an error status when an operation or a combination of operation and |
183 | /// data types is unimplemented |
184 | template <typename... Args> |
185 | static Status NotImplemented(Args&&... args) { |
186 | return Status(StatusCode::NotImplemented, |
187 | util::StringBuilder(std::forward<Args>(args)...)); |
188 | } |
189 | |
190 | /// Return an error status for invalid data (for example a string that fails parsing) |
191 | template <typename... Args> |
192 | static Status Invalid(Args&&... args) { |
193 | return Status(StatusCode::Invalid, util::StringBuilder(std::forward<Args>(args)...)); |
194 | } |
195 | |
196 | /// Return an error status when a container's capacity would exceed its limits |
197 | template <typename... Args> |
198 | static Status CapacityError(Args&&... args) { |
199 | return Status(StatusCode::CapacityError, |
200 | util::StringBuilder(std::forward<Args>(args)...)); |
201 | } |
202 | |
203 | /// Return an error status when some IO-related operation failed |
204 | template <typename... Args> |
205 | static Status IOError(Args&&... args) { |
206 | return Status(StatusCode::IOError, util::StringBuilder(std::forward<Args>(args)...)); |
207 | } |
208 | |
209 | /// Return an error status when some (de)serialization operation failed |
210 | template <typename... Args> |
211 | static Status SerializationError(Args&&... args) { |
212 | return Status(StatusCode::SerializationError, |
213 | util::StringBuilder(std::forward<Args>(args)...)); |
214 | } |
215 | |
216 | template <typename... Args> |
217 | static Status RError(Args&&... args) { |
218 | return Status(StatusCode::RError, util::StringBuilder(std::forward<Args>(args)...)); |
219 | } |
220 | |
221 | template <typename... Args> |
222 | static Status PlasmaObjectExists(Args&&... args) { |
223 | return Status(StatusCode::PlasmaObjectExists, |
224 | util::StringBuilder(std::forward<Args>(args)...)); |
225 | } |
226 | |
227 | template <typename... Args> |
228 | static Status PlasmaObjectNonexistent(Args&&... args) { |
229 | return Status(StatusCode::PlasmaObjectNonexistent, |
230 | util::StringBuilder(std::forward<Args>(args)...)); |
231 | } |
232 | |
233 | template <typename... Args> |
234 | static Status PlasmaObjectAlreadySealed(Args&&... args) { |
235 | return Status(StatusCode::PlasmaObjectAlreadySealed, |
236 | util::StringBuilder(std::forward<Args>(args)...)); |
237 | } |
238 | |
239 | template <typename... Args> |
240 | static Status PlasmaStoreFull(Args&&... args) { |
241 | return Status(StatusCode::PlasmaStoreFull, |
242 | util::StringBuilder(std::forward<Args>(args)...)); |
243 | } |
244 | |
245 | static Status StillExecuting() { return Status(StatusCode::StillExecuting, "" ); } |
246 | |
247 | template <typename... Args> |
248 | static Status CodeGenError(Args&&... args) { |
249 | return Status(StatusCode::CodeGenError, |
250 | util::StringBuilder(std::forward<Args>(args)...)); |
251 | } |
252 | |
253 | template <typename... Args> |
254 | static Status ExpressionValidationError(Args&&... args) { |
255 | return Status(StatusCode::ExpressionValidationError, |
256 | util::StringBuilder(std::forward<Args>(args)...)); |
257 | } |
258 | |
259 | template <typename... Args> |
260 | static Status ExecutionError(Args&&... args) { |
261 | return Status(StatusCode::ExecutionError, |
262 | util::StringBuilder(std::forward<Args>(args)...)); |
263 | } |
264 | |
265 | /// Return true iff the status indicates success. |
266 | bool ok() const { return (state_ == NULLPTR); } |
267 | |
268 | /// Return true iff the status indicates an out-of-memory error. |
269 | bool IsOutOfMemory() const { return code() == StatusCode::OutOfMemory; } |
270 | /// Return true iff the status indicates a key lookup error. |
271 | bool IsKeyError() const { return code() == StatusCode::KeyError; } |
272 | /// Return true iff the status indicates invalid data. |
273 | bool IsInvalid() const { return code() == StatusCode::Invalid; } |
274 | /// Return true iff the status indicates an IO-related failure. |
275 | bool IsIOError() const { return code() == StatusCode::IOError; } |
276 | /// Return true iff the status indicates a container reaching capacity limits. |
277 | bool IsCapacityError() const { return code() == StatusCode::CapacityError; } |
278 | /// Return true iff the status indicates a type error. |
279 | bool IsTypeError() const { return code() == StatusCode::TypeError; } |
280 | /// Return true iff the status indicates an unknown error. |
281 | bool IsUnknownError() const { return code() == StatusCode::UnknownError; } |
282 | /// Return true iff the status indicates an unimplemented operation. |
283 | bool IsNotImplemented() const { return code() == StatusCode::NotImplemented; } |
284 | /// Return true iff the status indicates a (de)serialization failure |
285 | bool IsSerializationError() const { return code() == StatusCode::SerializationError; } |
286 | /// Return true iff the status indicates a R-originated error. |
287 | bool IsRError() const { return code() == StatusCode::RError; } |
288 | /// Return true iff the status indicates a Python-originated error. |
289 | bool IsPythonError() const { return code() == StatusCode::PythonError; } |
290 | /// Return true iff the status indicates an already existing Plasma object. |
291 | bool IsPlasmaObjectExists() const { return code() == StatusCode::PlasmaObjectExists; } |
292 | /// Return true iff the status indicates a non-existent Plasma object. |
293 | bool IsPlasmaObjectNonexistent() const { |
294 | return code() == StatusCode::PlasmaObjectNonexistent; |
295 | } |
296 | /// Return true iff the status indicates an already sealed Plasma object. |
297 | bool IsPlasmaObjectAlreadySealed() const { |
298 | return code() == StatusCode::PlasmaObjectAlreadySealed; |
299 | } |
300 | /// Return true iff the status indicates the Plasma store reached its capacity limit. |
301 | bool IsPlasmaStoreFull() const { return code() == StatusCode::PlasmaStoreFull; } |
302 | |
303 | bool IsStillExecuting() const { return code() == StatusCode::StillExecuting; } |
304 | |
305 | bool IsCodeGenError() const { return code() == StatusCode::CodeGenError; } |
306 | |
307 | bool IsExpressionValidationError() const { |
308 | return code() == StatusCode::ExpressionValidationError; |
309 | } |
310 | |
311 | bool IsExecutionError() const { return code() == StatusCode::ExecutionError; } |
312 | |
313 | /// \brief Return a string representation of this status suitable for printing. |
314 | /// |
315 | /// The string "OK" is returned for success. |
316 | std::string ToString() const; |
317 | |
318 | /// \brief Return a string representation of the status code, without the message |
319 | /// text or POSIX code information. |
320 | std::string CodeAsString() const; |
321 | |
322 | /// \brief Return the StatusCode value attached to this status. |
323 | StatusCode code() const { return ok() ? StatusCode::OK : state_->code; } |
324 | |
325 | /// \brief Return the specific error message attached to this status. |
326 | std::string message() const { return ok() ? "" : state_->msg; } |
327 | |
328 | private: |
329 | struct State { |
330 | StatusCode code; |
331 | std::string msg; |
332 | }; |
333 | // OK status has a `NULL` state_. Otherwise, `state_` points to |
334 | // a `State` structure containing the error code and message(s) |
335 | State* state_; |
336 | |
337 | void DeleteState() { |
338 | delete state_; |
339 | state_ = NULLPTR; |
340 | } |
341 | void CopyFrom(const Status& s); |
342 | inline void MoveFrom(Status& s); |
343 | }; |
344 | |
345 | static inline std::ostream& operator<<(std::ostream& os, const Status& x) { |
346 | os << x.ToString(); |
347 | return os; |
348 | } |
349 | |
350 | void Status::MoveFrom(Status& s) { |
351 | delete state_; |
352 | state_ = s.state_; |
353 | s.state_ = NULLPTR; |
354 | } |
355 | |
356 | Status::Status(const Status& s) |
357 | : state_((s.state_ == NULLPTR) ? NULLPTR : new State(*s.state_)) {} |
358 | |
359 | Status& Status::operator=(const Status& s) { |
360 | // The following condition catches both aliasing (when this == &s), |
361 | // and the common case where both s and *this are ok. |
362 | if (state_ != s.state_) { |
363 | CopyFrom(s); |
364 | } |
365 | return *this; |
366 | } |
367 | |
368 | Status::Status(Status&& s) noexcept : state_(s.state_) { s.state_ = NULLPTR; } |
369 | |
370 | Status& Status::operator=(Status&& s) noexcept { |
371 | MoveFrom(s); |
372 | return *this; |
373 | } |
374 | |
375 | /// \cond FALSE |
376 | // (note: emits warnings on Doxygen < 1.8.15, |
377 | // see https://github.com/doxygen/doxygen/issues/6295) |
378 | Status Status::operator&(const Status& s) const noexcept { |
379 | if (ok()) { |
380 | return s; |
381 | } else { |
382 | return *this; |
383 | } |
384 | } |
385 | |
386 | Status Status::operator&(Status&& s) const noexcept { |
387 | if (ok()) { |
388 | return std::move(s); |
389 | } else { |
390 | return *this; |
391 | } |
392 | } |
393 | |
394 | Status& Status::operator&=(const Status& s) noexcept { |
395 | if (ok() && !s.ok()) { |
396 | CopyFrom(s); |
397 | } |
398 | return *this; |
399 | } |
400 | |
401 | Status& Status::operator&=(Status&& s) noexcept { |
402 | if (ok() && !s.ok()) { |
403 | MoveFrom(s); |
404 | } |
405 | return *this; |
406 | } |
407 | /// \endcond |
408 | |
409 | } // namespace arrow |
410 | |
411 | #endif // ARROW_STATUS_H_ |
412 | |