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
79namespace arrow {
80
81enum 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.
107class 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.
118class 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
345static inline std::ostream& operator<<(std::ostream& os, const Status& x) {
346 os << x.ToString();
347 return os;
348}
349
350void Status::MoveFrom(Status& s) {
351 delete state_;
352 state_ = s.state_;
353 s.state_ = NULLPTR;
354}
355
356Status::Status(const Status& s)
357 : state_((s.state_ == NULLPTR) ? NULLPTR : new State(*s.state_)) {}
358
359Status& 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
368Status::Status(Status&& s) noexcept : state_(s.state_) { s.state_ = NULLPTR; }
369
370Status& 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)
378Status Status::operator&(const Status& s) const noexcept {
379 if (ok()) {
380 return s;
381 } else {
382 return *this;
383 }
384}
385
386Status Status::operator&(Status&& s) const noexcept {
387 if (ok()) {
388 return std::move(s);
389 } else {
390 return *this;
391 }
392}
393
394Status& Status::operator&=(const Status& s) noexcept {
395 if (ok() && !s.ok()) {
396 CopyFrom(s);
397 }
398 return *this;
399}
400
401Status& 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