| /* | |
| * 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/apache-arrow-17.0.0/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 |