blob: 41f7663fae1847b0016968ce7df7dfd5bba338e8 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
#ifndef IMPALA_COMMON_STATUS_H
#define IMPALA_COMMON_STATUS_H
#include <iosfwd>
#include <string>
#include <vector>
#include "common/compiler-util.h"
#include "common/logging.h"
#include "gen-cpp/Status_types.h" // for TStatus
#include "gen-cpp/ErrorCodes_types.h" // for TErrorCode
#include "gen-cpp/TCLIService_types.h" // for HS2 TStatus
#include "util/error-util.h" // for ErrorMessage
#define STATUS_API_VERSION 2
namespace impala {
class StatusPB;
/// Status is used as a function return type to indicate success, failure or cancellation
/// of the function. In case of successful completion, it only occupies sizeof(void*)
/// statically allocated memory and therefore no more members should be added to this
/// class.
//
/// A Status may either be OK (represented by passing a default constructed Status
/// instance, created via Status::OK()), or it may represent an error condition. In the
/// latter case, a Status has both an error code, which belongs to the TErrorCode enum,
/// and an error string, which may be presented to clients or logged to disk.
///
/// An error Status may also have one or more optional 'detail' strings which provide
/// further context. These strings are intended for internal consumption only - and
/// therefore will not be sent to clients.
///
/// The state associated with an error Status is encapsulated in an ErrorMsg instance to
/// ensure that the size of Status is kept very small, as it is passed around on the stack
/// as a return value. See ErrorMsg for more details on how error strings are constructed.
///
/// Example Usage:
/// Status fnB(int x) {
//
/// // Status as return value
/// Status status = fnA(x);
/// if (!status.ok()) {
/// status.AddDetail("fnA(x) went wrong");
/// return status;
/// }
//
/// int r = Read(fid);
/// // Attaches an ErrorMsg with type GENERAL to the status
/// if (r == -1) return Status("String Constructor");
//
/// int x = MoreRead(x);
/// if (x == 4711) {
/// // Specific error messages with one param
/// Status s = Status(ERROR_4711_HAS_NO_BLOCKS, x);
/// // Optional detail
/// s.AddDetail("rotation-disk-broken due to weather");
/// }
///
/// return Status::OK();
/// }
class NODISCARD Status {
public:
typedef strings::internal::SubstituteArg ArgType;
ALWAYS_INLINE Status(): msg_(NULL) {}
// Return a default constructed Status instance in the OK case.
static ALWAYS_INLINE Status OK() { return Status(); }
// Return a MEM_LIMIT_EXCEEDED error status.
static Status MemLimitExceeded();
static Status MemLimitExceeded(const std::string& details);
static const Status CANCELLED;
static const Status DEPRECATED_RPC;
/// Return a CANCELLED_INTERNALLY error. This should be used for "internal"
/// cancellation messages that were not user-initiated and are not expected to be shown
/// to the client. 'subsystem' is the name of the subsystem in which the cancellation
/// occurred, so that we can determine where the cancellation came from.
static Status CancelledInternal(const char* subsystem);
/// Copy c'tor makes copy of error detail so Status can be returned by value.
ALWAYS_INLINE Status(const Status& status) : msg_(NULL) {
if (UNLIKELY(status.msg_ != NULL)) CopyMessageFrom(status);
}
/// Move constructor that moves the error message (if any) and resets 'other' to the
/// default OK Status.
ALWAYS_INLINE Status(Status&& other) noexcept : msg_(other.msg_) { other.msg_ = NULL; }
/// Status using only the error code as a parameter. This can be used for error messages
/// that don't take format parameters.
explicit Status(TErrorCode::type code) : Status(false, code) {}
/// These constructors are used if the caller wants to indicate a non-successful
/// execution and supply a client-facing error message. Using TErrorCode and
/// parameterised error messages is the preferred way of instantiating a
/// non-successful Status instead of the std::string constructor. These constructors
/// log the error message and a traceback, which can be expensive, so these should only
/// be used for rare, low-frequency errors. Status::Expected() does not log a traceback
/// and should be used for higher-frequency errors.
Status(TErrorCode::type error, const ArgType& arg0) : Status(false, error, arg0) {}
Status(TErrorCode::type error, const ArgType& arg0, const ArgType& arg1)
: Status(false, error, arg0, arg1) {}
Status(TErrorCode::type error, const ArgType& arg0, const ArgType& arg1,
const ArgType& arg2)
: Status(false, error, arg0, arg1, arg2) {}
Status(TErrorCode::type error, const ArgType& arg0, const ArgType& arg1,
const ArgType& arg2, const ArgType& arg3)
: Status(false, error, arg0, arg1, arg2, arg3) {}
Status(TErrorCode::type error, const ArgType& arg0, const ArgType& arg1,
const ArgType& arg2, const ArgType& arg3, const ArgType& arg4)
: Status(false, error, arg0, arg1, arg2, arg3, arg4) {}
Status(TErrorCode::type error, const ArgType& arg0, const ArgType& arg1,
const ArgType& arg2, const ArgType& arg3, const ArgType& arg4, const ArgType& arg5)
: Status(false, error, arg0, arg1, arg2, arg3, arg4, arg5) {}
Status(TErrorCode::type error, const ArgType& arg0, const ArgType& arg1,
const ArgType& arg2, const ArgType& arg3, const ArgType& arg4, const ArgType& arg5,
const ArgType& arg6)
: Status(false, error, arg0, arg1, arg2, arg3, arg4, arg5, arg6) {}
Status(TErrorCode::type error, const ArgType& arg0, const ArgType& arg1,
const ArgType& arg2, const ArgType& arg3, const ArgType& arg4, const ArgType& arg5,
const ArgType& arg6, const ArgType& arg7)
: Status(false, error, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) {}
Status(TErrorCode::type error, const ArgType& arg0, const ArgType& arg1,
const ArgType& arg2, const ArgType& arg3, const ArgType& arg4, const ArgType& arg5,
const ArgType& arg6, const ArgType& arg7, const ArgType& arg8)
: Status(false, error, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) {}
Status(TErrorCode::type error, const ArgType& arg0, const ArgType& arg1,
const ArgType& arg2, const ArgType& arg3, const ArgType& arg4, const ArgType& arg5,
const ArgType& arg6, const ArgType& arg7, const ArgType& arg8, const ArgType& arg9)
: Status(false, error, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) {}
/// Used when the ErrorMsg is created as an intermediate value that is either passed to
/// the Status or to the RuntimeState.
explicit Status(const ErrorMsg& e);
/// This constructor creates a Status with a default error code of GENERAL and is not
/// intended for statuses that might be client-visible.
/// TODO: deprecate
explicit Status(const std::string& error_msg);
/// "Copy" c'tor from TStatus.
/// Retains the TErrorCode value and the message
explicit Status(const TStatus& status);
/// "Copy" c'tor from StatusPB (a protobuf serialized version of Status object).
/// Retains the TErrorCode value and the message
explicit Status(const StatusPB& status);
/// "Copy c'tor from HS2 TStatus.
/// Retains the TErrorCode value and the message
explicit Status(const apache::hive::service::cli::thrift::TStatus& hs2_status);
/// The below Status::Expected() functions create a status instance that represents
/// an expected error. They behave the same as the constructors with the same
/// argument types, except they do not log the error message and traceback.
static Status Expected(const ErrorMsg& e);
static Status Expected(const std::string& error_msg);
static Status Expected(TErrorCode::type error);
static Status Expected(TErrorCode::type error, const ArgType& arg0);
static Status Expected(
TErrorCode::type error, const ArgType& arg0, const ArgType& arg1);
static Status Expected(TErrorCode::type error, const ArgType& arg0,
const ArgType& arg1, const ArgType& arg2);
static Status Expected(TErrorCode::type error, const ArgType& arg0,
const ArgType& arg1, const ArgType& arg2, const ArgType& arg3);
static Status Expected(TErrorCode::type error, const ArgType& arg0,
const ArgType& arg1, const ArgType& arg2, const ArgType& arg3, const ArgType& arg4);
static Status Expected(TErrorCode::type error, const ArgType& arg0,
const ArgType& arg1, const ArgType& arg2, const ArgType& arg3, const ArgType& arg4,
const ArgType& arg5);
static Status Expected(TErrorCode::type error, const ArgType& arg0,
const ArgType& arg1, const ArgType& arg2, const ArgType& arg3, const ArgType& arg4,
const ArgType& arg5, const ArgType& arg6);
static Status Expected(TErrorCode::type error, const ArgType& arg0,
const ArgType& arg1, const ArgType& arg2, const ArgType& arg3, const ArgType& arg4,
const ArgType& arg5, const ArgType& arg6, const ArgType& arg7);
static Status Expected(TErrorCode::type error, const ArgType& arg0,
const ArgType& arg1, const ArgType& arg2, const ArgType& arg3, const ArgType& arg4,
const ArgType& arg5, const ArgType& arg6, const ArgType& arg7, const ArgType& arg8);
static Status Expected(TErrorCode::type error, const ArgType& arg0,
const ArgType& arg1, const ArgType& arg2, const ArgType& arg3, const ArgType& arg4,
const ArgType& arg5, const ArgType& arg6, const ArgType& arg7, const ArgType& arg8,
const ArgType& arg9);
/// same as copy c'tor
ALWAYS_INLINE Status& operator=(const Status& status) {
// Take the slow path if either Status objects have non-NULL messages (unless they
// are aliases).
if (UNLIKELY(msg_ != status.msg_)) CopyMessageFrom(status);
return *this;
}
/// Move assignment that moves the error message (if any) and resets 'other' to the
/// default OK Status.
ALWAYS_INLINE Status& operator=(Status&& other) {
if (UNLIKELY(msg_ != NULL)) FreeMessage();
msg_ = other.msg_;
other.msg_ = NULL;
return *this;
}
ALWAYS_INLINE ~Status() {
// The UNLIKELY and inlining here are important hints for the compiler to
// streamline the common case of Status::OK(). Use FreeMessage() which is
// not inlined to free the message. This avoids potential code bloat due
// to duplicated code from the delete statement.
if (UNLIKELY(msg_ != NULL)) FreeMessage();
}
/// Retains the TErrorCode value and the message
Status& operator=(const TStatus& status);
bool ALWAYS_INLINE ok() const { return msg_ == NULL; }
/// Return true if this is a user-initiated or internal cancellation.
bool IsCancelled() const {
return msg_ != NULL && (msg_->error() == TErrorCode::CANCELLED
|| msg_->error() == TErrorCode::CANCELLED_INTERNALLY);
}
bool IsMemLimitExceeded() const {
return msg_ != NULL
&& msg_->error() == TErrorCode::MEM_LIMIT_EXCEEDED;
}
bool IsInternalError() const {
return msg_ != NULL
&& msg_->error() == TErrorCode::INTERNAL_ERROR;
}
bool IsRecoverableError() const {
return msg_ != NULL
&& msg_->error() == TErrorCode::RECOVERABLE_ERROR;
}
bool IsDiskIoError() const {
return msg_ != NULL
&& msg_->error() == TErrorCode::DISK_IO_ERROR;
}
bool IsThreadPoolError() const {
return msg_ != NULL
&& (msg_->error() == TErrorCode::THREAD_POOL_SUBMIT_FAILED ||
msg_->error() == TErrorCode::THREAD_POOL_TASK_TIMED_OUT);
}
/// Returns the error message associated with a non-successful status.
const ErrorMsg& msg() const {
DCHECK(msg_ != NULL);
return *msg_; // NOLINT: clang-tidy thinks this might deref a nullptr
}
/// Add a detail string. Calling this method is only defined on a non-OK message
void AddDetail(const std::string& msg);
/// Does nothing if status.ok().
/// Otherwise: if 'this' is an error status, adds the error msg from 'status';
/// otherwise assigns 'status'.
void MergeStatus(const Status& status);
/// Convert into TStatus. Call this if 'status_container' contains an optional TStatus
/// field named 'status'. This also sets status_container->__isset.status.
template <typename T> void SetTStatus(T* status_container) const {
ToThrift(&status_container->status);
status_container->__isset.status = true;
}
/// Convert into TStatus.
void ToThrift(TStatus* status) const;
/// Serialize into StatusPB
void ToProto(StatusPB* status) const;
/// Returns the formatted message of the error message and the individual details of the
/// additional messages as a single string. This should only be called internally and
/// not to report an error back to the client.
const std::string GetDetail() const;
TErrorCode::type code() const {
return msg_ == NULL ? TErrorCode::OK : msg_->error();
}
static const char* LLVM_CLASS_NAME;
private:
// Status constructors that can suppress logging via 'silent' parameter
Status(const ErrorMsg& error_msg, bool silent);
Status(const std::string& error_msg, bool silent);
Status(bool silent, TErrorCode::type code);
Status(bool silent, TErrorCode::type error, const ArgType& arg0);
Status(bool silent, TErrorCode::type error, const ArgType& arg0, const ArgType& arg1);
Status(bool silent, TErrorCode::type error, const ArgType& arg0, const ArgType& arg1,
const ArgType& arg2);
Status(bool silent, TErrorCode::type error, const ArgType& arg0, const ArgType& arg1,
const ArgType& arg2, const ArgType& arg3);
Status(bool silent, TErrorCode::type error, const ArgType& arg0, const ArgType& arg1,
const ArgType& arg2, const ArgType& arg3, const ArgType& arg4);
Status(bool silent, TErrorCode::type error, const ArgType& arg0, const ArgType& arg1,
const ArgType& arg2, const ArgType& arg3, const ArgType& arg4, const ArgType& arg5);
Status(bool silent, TErrorCode::type error, const ArgType& arg0, const ArgType& arg1,
const ArgType& arg2, const ArgType& arg3, const ArgType& arg4, const ArgType& arg5,
const ArgType& arg6);
Status(bool silent, TErrorCode::type error, const ArgType& arg0, const ArgType& arg1,
const ArgType& arg2, const ArgType& arg3, const ArgType& arg4, const ArgType& arg5,
const ArgType& arg6, const ArgType& arg7);
Status(bool silent, TErrorCode::type error, const ArgType& arg0, const ArgType& arg1,
const ArgType& arg2, const ArgType& arg3, const ArgType& arg4, const ArgType& arg5,
const ArgType& arg6, const ArgType& arg7, const ArgType& arg8);
Status(bool silent, TErrorCode::type error, const ArgType& arg0, const ArgType& arg1,
const ArgType& arg2, const ArgType& arg3, const ArgType& arg4, const ArgType& arg5,
const ArgType& arg6, const ArgType& arg7, const ArgType& arg8, const ArgType& arg9);
// A non-inline function for copying status' message.
void CopyMessageFrom(const Status& status) noexcept;
// A non-inline function for freeing status' message.
void FreeMessage() noexcept;
/// A non-inline function for unwrapping a TStatus object.
void FromThrift(const TStatus& status);
/// A non-inline function for unwrapping a StatusPB object.
void FromProto(const StatusPB& status);
/// Status uses a naked pointer to ensure the size of an instance on the stack is only
/// the sizeof(ErrorMsg*). Every Status owns its ErrorMsg instance.
ErrorMsg* msg_;
};
/// for debugging
std::ostream& operator<<(std::ostream& os, const Status& status);
/// some generally useful macros
#define RETURN_IF_ERROR(stmt) \
do { \
const ::impala::Status& _status = (stmt); \
if (UNLIKELY(!_status.ok())) return _status; \
} while (false)
#define LOG_AND_RETURN_IF_ERROR(stmt) \
do { \
const ::impala::Status& _status = (stmt); \
if (UNLIKELY(!_status.ok())) { \
LOG(INFO) << _status.GetDetail(); \
return _status; \
} \
} while (false)
#define RETURN_VOID_IF_ERROR(stmt) \
do { \
if (UNLIKELY(!(stmt).ok())) return; \
} while (false)
#define ABORT_IF_ERROR(stmt) \
do { \
const ::impala::Status& _status = (stmt); \
if (UNLIKELY(!_status.ok())) { \
ABORT_WITH_ERROR(_status.GetDetail()); \
} \
} while (false)
// Log to FATAL and abort process, generating a core dump if enabled. This should be used
// for unexpected error cases where we want a core dump.
// LOG(FATAL) will call abort().
#define ABORT_WITH_ERROR(msg) \
do { \
LOG(FATAL) << msg << ". Impalad exiting.\n"; \
} while (false)
// Log to ERROR and exit process with status 1 without calling abort() or dumping core.
// This should be used for expected error cases, e.g. bad command-line arguments where
// we just want to exit cleanly without generating core dumps.
#define CLEAN_EXIT_WITH_ERROR(msg) \
do { \
LOG(ERROR) << msg << ". Impalad exiting.\n"; \
google::FlushLogFiles(google::ERROR); \
exit(1); \
} while (false)
/// This macro can be appended to a function definition to generate a compiler warning
/// if the result is ignored.
/// TODO: when we upgrade gcc from 4.9.2, we may be able to apply this to the Status
/// type to get this automatically for all Status-returning functions.
#define WARN_UNUSED_RESULT __attribute__((warn_unused_result))
}
#endif