| /* |
| * 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. |
| */ |
| |
| // Copyright (c) 2011 The LevelDB Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. See the AUTHORS file for names of contributors. |
| |
| // Adapted from Apache Arrow |
| // https://github.com/apache/arrow/blob/main/cpp/src/arrow/status.h |
| |
| // A Status encapsulates the result of an operation. It may indicate success, |
| // or it may indicate an error with an associated error message. |
| // |
| // Multiple threads can invoke const methods on a Status without |
| // external synchronization, but if any of the threads may call a |
| // non-const method, all threads accessing the same Status must use |
| // external synchronization. |
| |
| #pragma once |
| |
| #include <cstring> |
| #include <iosfwd> |
| #include <memory> |
| #include <string> |
| #include <utility> |
| |
| #include "paimon/compare.h" |
| #include "paimon/macros.h" |
| #include "paimon/string_builder.h" |
| #include "paimon/visibility.h" |
| |
| namespace paimon { |
| |
| enum class StatusCode : char { |
| OK = 0, |
| OutOfMemory = 1, |
| KeyError = 2, |
| TypeError = 3, |
| Invalid = 4, |
| IOError = 5, |
| CapacityError = 6, |
| IndexError = 7, |
| Cancelled = 8, |
| UnknownError = 9, |
| NotImplemented = 10, |
| SerializationError = 11, |
| NotExist = 12, |
| Exist = 13, |
| }; |
| |
| /// \brief An opaque class that allows subsystems to retain |
| /// additional information inside the Status. |
| class PAIMON_EXPORT StatusDetail { |
| public: |
| virtual ~StatusDetail() = default; |
| /// \brief Return a unique id for the type of the StatusDetail |
| /// (effectively a poor man's substitute for RTTI). |
| virtual const char* type_id() const = 0; |
| /// \brief Produce a human-readable description of this status. |
| virtual std::string ToString() const = 0; |
| |
| bool operator==(const StatusDetail& other) const noexcept { |
| return std::string(type_id()) == other.type_id() && ToString() == other.ToString(); |
| } |
| }; |
| |
| /// \brief Status outcome object (success or error) |
| /// |
| /// The Status object is an object holding the outcome of an operation. |
| /// The outcome is represented as a StatusCode, either success |
| /// (StatusCode::OK) or an error (any other of the StatusCode enumeration values). |
| /// |
| /// Additionally, if an error occurred, a specific error message is generally |
| /// attached. |
| class PAIMON_MUST_USE_TYPE PAIMON_EXPORT Status : public util::EqualityComparable<Status>, |
| public util::ToStringOstreamable<Status> { |
| public: |
| // Create a success status. |
| Status() noexcept = default; |
| ~Status() noexcept { |
| // PAIMON-2400: On certain compilers, splitting off the slow path improves |
| // performance significantly. |
| if (PAIMON_PREDICT_FALSE(state_ != nullptr)) { |
| DeleteState(); |
| } |
| } |
| |
| Status(StatusCode code, const std::string& msg); |
| /// \brief Pluggable constructor for use by sub-systems. detail cannot be null. |
| Status(StatusCode code, std::string msg, std::shared_ptr<StatusDetail> detail); |
| |
| // Copy the specified status. |
| inline Status(const Status& s); |
| inline Status& operator=(const Status& s); |
| |
| // Move the specified status. |
| inline Status(Status&& s) noexcept; |
| inline Status& operator=(Status&& s) noexcept; |
| |
| inline bool Equals(const Status& s) const; |
| |
| // AND the statuses. |
| inline Status operator&(const Status& s) const noexcept; |
| inline Status operator&(Status&& s) const noexcept; |
| inline Status& operator&=(const Status& s) noexcept; |
| inline Status& operator&=(Status&& s) noexcept; |
| |
| /// Return a success status |
| static Status OK() { |
| return {}; |
| } |
| |
| template <typename... Args> |
| static Status FromArgs(StatusCode code, Args&&... args) { |
| return Status(code, util::StringBuilder(std::forward<Args>(args)...)); |
| } |
| |
| template <typename... Args> |
| static Status FromDetailAndArgs(StatusCode code, std::shared_ptr<StatusDetail> detail, |
| Args&&... args) { |
| return Status(code, util::StringBuilder(std::forward<Args>(args)...), std::move(detail)); |
| } |
| |
| /// Return an error status for out-of-memory conditions |
| template <typename... Args> |
| static Status OutOfMemory(Args&&... args) { |
| return Status::FromArgs(StatusCode::OutOfMemory, std::forward<Args>(args)...); |
| } |
| |
| /// Return an error status for failed key lookups (e.g. column name in a table) |
| template <typename... Args> |
| static Status KeyError(Args&&... args) { |
| return Status::FromArgs(StatusCode::KeyError, std::forward<Args>(args)...); |
| } |
| |
| /// Return an error status for type errors (such as mismatching data types) |
| template <typename... Args> |
| static Status TypeError(Args&&... args) { |
| return Status::FromArgs(StatusCode::TypeError, std::forward<Args>(args)...); |
| } |
| |
| /// Return an error status for unknown errors |
| template <typename... Args> |
| static Status UnknownError(Args&&... args) { |
| return Status::FromArgs(StatusCode::UnknownError, std::forward<Args>(args)...); |
| } |
| |
| /// Return an error status when an operation or a combination of operation and |
| /// data types is unimplemented |
| template <typename... Args> |
| static Status NotImplemented(Args&&... args) { |
| return Status::FromArgs(StatusCode::NotImplemented, std::forward<Args>(args)...); |
| } |
| |
| /// Return an error status for invalid data (for example a string that fails parsing) |
| template <typename... Args> |
| static Status Invalid(Args&&... args) { |
| return Status::FromArgs(StatusCode::Invalid, std::forward<Args>(args)...); |
| } |
| |
| /// Return an error status when an index is out of bounds |
| template <typename... Args> |
| static Status IndexError(Args&&... args) { |
| return Status::FromArgs(StatusCode::IndexError, std::forward<Args>(args)...); |
| } |
| |
| /// Return an error status for cancelled operation |
| template <typename... Args> |
| static Status Cancelled(Args&&... args) { |
| return Status::FromArgs(StatusCode::Cancelled, std::forward<Args>(args)...); |
| } |
| |
| /// Return an error status when a container's capacity would exceed its limits |
| template <typename... Args> |
| static Status CapacityError(Args&&... args) { |
| return Status::FromArgs(StatusCode::CapacityError, std::forward<Args>(args)...); |
| } |
| |
| /// Return an error status when some IO-related operation failed |
| template <typename... Args> |
| static Status IOError(Args&&... args) { |
| return Status::FromArgs(StatusCode::IOError, std::forward<Args>(args)...); |
| } |
| |
| /// Return an error status when some (de)serialization operation failed |
| template <typename... Args> |
| static Status SerializationError(Args&&... args) { |
| return Status::FromArgs(StatusCode::SerializationError, std::forward<Args>(args)...); |
| } |
| |
| /// Return an error status when something is not existed. |
| template <typename... Args> |
| static Status NotExist(Args&&... args) { |
| return Status::FromArgs(StatusCode::NotExist, std::forward<Args>(args)...); |
| } |
| |
| /// Return an error status when something is already existed. |
| template <typename... Args> |
| static Status Exist(Args&&... args) { |
| return Status::FromArgs(StatusCode::Exist, std::forward<Args>(args)...); |
| } |
| |
| /// Return true iff the status indicates success. |
| bool ok() const { |
| return (state_ == nullptr); |
| } |
| |
| /// Return true iff the status indicates an out-of-memory error. |
| bool IsOutOfMemory() const { |
| return code() == StatusCode::OutOfMemory; |
| } |
| /// Return true iff the status indicates a key lookup error. |
| bool IsKeyError() const { |
| return code() == StatusCode::KeyError; |
| } |
| /// Return true iff the status indicates invalid data. |
| bool IsInvalid() const { |
| return code() == StatusCode::Invalid; |
| } |
| /// Return true iff the status indicates a cancelled operation. |
| bool IsCancelled() const { |
| return code() == StatusCode::Cancelled; |
| } |
| /// Return true iff the status indicates an IO-related failure. |
| bool IsIOError() const { |
| return code() == StatusCode::IOError; |
| } |
| /// Return true iff the status indicates a container reaching capacity limits. |
| bool IsCapacityError() const { |
| return code() == StatusCode::CapacityError; |
| } |
| /// Return true iff the status indicates an out of bounds index. |
| bool IsIndexError() const { |
| return code() == StatusCode::IndexError; |
| } |
| /// Return true iff the status indicates a type error. |
| bool IsTypeError() const { |
| return code() == StatusCode::TypeError; |
| } |
| /// Return true iff the status indicates an unknown error. |
| bool IsUnknownError() const { |
| return code() == StatusCode::UnknownError; |
| } |
| /// Return true iff the status indicates an unimplemented operation. |
| bool IsNotImplemented() const { |
| return code() == StatusCode::NotImplemented; |
| } |
| /// Return true iff the status indicates a (de)serialization failure |
| bool IsSerializationError() const { |
| return code() == StatusCode::SerializationError; |
| } |
| /// Return true iff the status indicates a not exist error. |
| bool IsNotExist() const { |
| return code() == StatusCode::NotExist; |
| } |
| /// Return true iff the status indicates an exist error. |
| bool IsExist() const { |
| return code() == StatusCode::Exist; |
| } |
| |
| /// \brief Return a string representation of this status suitable for printing. |
| /// |
| /// The string "OK" is returned for success. |
| std::string ToString() const; |
| |
| /// \brief Return a string representation of the status code, without the message |
| /// text or POSIX code information. |
| std::string CodeAsString() const; |
| static std::string CodeAsString(StatusCode); |
| |
| /// \brief Return the StatusCode value attached to this status. |
| StatusCode code() const { |
| return ok() ? StatusCode::OK : state_->code; |
| } |
| |
| /// \brief Return the specific error message attached to this status. |
| std::string message() const { |
| return ok() ? "" : state_->msg; |
| } |
| |
| /// \brief Return the status detail attached to this message. |
| const std::shared_ptr<StatusDetail>& detail() const { |
| static std::shared_ptr<StatusDetail> no_detail = nullptr; |
| return state_ ? state_->detail : no_detail; |
| } |
| |
| /// \brief Return a new Status copying the existing status, but |
| /// updating with the existing detail. |
| Status WithDetail(std::shared_ptr<StatusDetail> new_detail) const { |
| return {code(), message(), std::move(new_detail)}; |
| } |
| |
| /// \brief Return a new Status with changed message, copying the |
| /// existing status code and detail. |
| template <typename... Args> |
| Status WithMessage(Args&&... args) const { |
| return FromArgs(code(), std::forward<Args>(args)...).WithDetail(detail()); |
| } |
| |
| [[noreturn]] void Abort() const; |
| [[noreturn]] void Abort(const std::string& message) const; |
| |
| #ifdef PAIMON_EXTRA_ERROR_CONTEXT |
| void AddContextLine(const char* filename, int line, const char* function_name, |
| const char* expr); |
| #endif |
| |
| private: |
| struct State { |
| StatusCode code; |
| std::string msg; |
| std::shared_ptr<StatusDetail> detail; |
| }; |
| // OK status has a `NULL` state_. Otherwise, `state_` points to |
| // a `State` structure containing the error code and message(s) |
| State* state_{nullptr}; |
| |
| void DeleteState() { |
| delete state_; |
| state_ = nullptr; |
| } |
| void CopyFrom(const Status& s); |
| inline void MoveFrom(Status& s); |
| }; |
| |
| void Status::MoveFrom(Status& s) { |
| delete state_; |
| state_ = s.state_; |
| s.state_ = nullptr; |
| } |
| |
| Status::Status(const Status& s) : state_((s.state_ == nullptr) ? nullptr : new State(*s.state_)) {} |
| |
| Status& Status::operator=(const Status& s) { |
| // The following condition catches both aliasing (when this == &s), |
| // and the common case where both s and *this are ok. |
| if (state_ != s.state_) { |
| CopyFrom(s); |
| } |
| return *this; |
| } |
| |
| Status::Status(Status&& s) noexcept : state_(s.state_) { |
| s.state_ = nullptr; |
| } |
| |
| Status& Status::operator=(Status&& s) noexcept { |
| MoveFrom(s); |
| return *this; |
| } |
| |
| bool Status::Equals(const Status& s) const { |
| if (state_ == s.state_) { |
| return true; |
| } |
| |
| if (ok() || s.ok()) { |
| return false; |
| } |
| |
| if (detail() != s.detail()) { |
| if ((detail() && !s.detail()) || (!detail() && s.detail())) { |
| return false; |
| } |
| return *detail() == *s.detail(); |
| } |
| |
| return code() == s.code() && message() == s.message(); |
| } |
| |
| /// \cond FALSE |
| // (note: emits warnings on Doxygen < 1.8.15, |
| // see https://github.com/doxygen/doxygen/issues/6295) |
| Status Status::operator&(const Status& s) const noexcept { |
| if (ok()) { |
| return s; |
| } else { |
| return *this; |
| } |
| } |
| |
| Status Status::operator&(Status&& s) const noexcept { |
| if (ok()) { |
| return std::move(s); |
| } else { |
| return *this; |
| } |
| } |
| |
| Status& Status::operator&=(const Status& s) noexcept { |
| if (ok() && !s.ok()) { |
| CopyFrom(s); |
| } |
| return *this; |
| } |
| |
| Status& Status::operator&=(Status&& s) noexcept { |
| if (ok() && !s.ok()) { |
| MoveFrom(s); |
| } |
| return *this; |
| } |
| /// \endcond |
| |
| namespace internal { |
| |
| // Extract Status from Status or Result<T> |
| // Useful for the status check macros such as RETURN_NOT_OK. |
| inline Status GenericToStatus(const Status& st) { |
| return st; |
| } |
| inline Status GenericToStatus(Status&& st) { |
| return std::move(st); |
| } |
| |
| } // namespace internal |
| |
| #ifdef PAIMON_EXTRA_ERROR_CONTEXT |
| |
| /// \brief Return with given status if condition is met. |
| #define PAIMON_RETURN_IF_(condition, status, expr) \ |
| do { \ |
| if (PAIMON_PREDICT_FALSE(condition)) { \ |
| ::paimon::Status _st = (status); \ |
| _st.AddContextLine(__FILE__, __LINE__, __FUNCTION__, expr); \ |
| return _st; \ |
| } \ |
| } while (0) |
| #else |
| |
| #define PAIMON_RETURN_IF_(condition, status, _) \ |
| do { \ |
| if (PAIMON_PREDICT_FALSE(condition)) { \ |
| return (status); \ |
| } \ |
| } while (0) |
| |
| #endif // PAIMON_EXTRA_ERROR_CONTEXT |
| |
| #define PAIMON_RETURN_IF(condition, status) \ |
| PAIMON_RETURN_IF_(condition, status, PAIMON_STRINGIFY(status)) |
| |
| /// \brief Propagate any non-successful Status to the caller |
| #define PAIMON_RETURN_NOT_OK(status) \ |
| do { \ |
| ::paimon::Status __s = ::paimon::internal::GenericToStatus(status); \ |
| PAIMON_RETURN_IF_(!__s.ok(), __s, PAIMON_STRINGIFY(status)); \ |
| } while (false) |
| |
| #define PAIMON_RETURN_NOT_OK_ELSE(s, else_) \ |
| do { \ |
| ::paimon::Status _s = ::paimon::internal::GenericToStatus(s); \ |
| if (!_s.ok()) { \ |
| else_; \ |
| return _s; \ |
| } \ |
| } while (false) |
| |
| } // namespace paimon |