#pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Poco { class Logger; } namespace DB { class AtomicLogger; /// This flag can be set for testing purposes - to check that no exceptions are thrown. extern bool terminate_on_any_exception; /// This flag controls if error statistics should be updated when an exception is thrown. These /// statistics are shown for example in system.errors. Defaults to true. If the error is internal, /// non-critical, and handled otherwise it is useful to disable the statistics update and not /// alarm the user needlessly. extern thread_local bool update_error_statistics; /// Disable the update of error statistics #define DO_NOT_UPDATE_ERROR_STATISTICS() \ update_error_statistics = false; \ SCOPE_EXIT({ update_error_statistics = true; }) class Exception : public Poco::Exception { public: using FramePointers = std::vector; Exception() { if (terminate_on_any_exception) std::terminate(); capture_thread_frame_pointers = getThreadFramePointers(); } Exception(const PreformattedMessage & msg, int code): Exception(msg.text, code) { if (terminate_on_any_exception) std::terminate(); capture_thread_frame_pointers = getThreadFramePointers(); message_format_string = msg.format_string; message_format_string_args = msg.format_string_args; } Exception(PreformattedMessage && msg, int code): Exception(std::move(msg.text), code) { if (terminate_on_any_exception) std::terminate(); capture_thread_frame_pointers = getThreadFramePointers(); message_format_string = msg.format_string; message_format_string_args = msg.format_string_args; } /// Collect call stacks of all previous jobs' schedulings leading to this thread job's execution static thread_local bool enable_job_stack_trace; static thread_local bool can_use_thread_frame_pointers; /// Because of unknown order of static destructor calls, /// thread_frame_pointers can already be uninitialized when a different destructor generates an exception. /// To prevent such scenarios, a wrapper class is created and a function that will return empty vector /// if its destructor is already called using ThreadFramePointersBase = std::vector; struct ThreadFramePointers { ThreadFramePointers(); ~ThreadFramePointers(); ThreadFramePointersBase frame_pointers; }; static ThreadFramePointersBase getThreadFramePointers(); static void setThreadFramePointers(ThreadFramePointersBase frame_pointers); /// Callback for any exception static std::function callback; protected: static thread_local ThreadFramePointers thread_frame_pointers; // used to remove the sensitive information from exceptions if query_masking_rules is configured struct MessageMasked { std::string msg; explicit MessageMasked(const std::string & msg_); explicit MessageMasked(std::string && msg_); }; Exception(const MessageMasked & msg_masked, int code, bool remote_); Exception(MessageMasked && msg_masked, int code, bool remote_); // delegating constructor to mask sensitive information from the message Exception(const std::string & msg, int code, bool remote_ = false): Exception(MessageMasked(msg), code, remote_) {} Exception(std::string && msg, int code, bool remote_ = false): Exception(MessageMasked(std::move(msg)), code, remote_) {} public: /// This creator is for exceptions that should format a message using fmt::format from the variadic ctor Exception(code, fmt, ...), /// but were not rewritten yet. It will be removed. static Exception createDeprecated(const std::string & msg, int code, bool remote_ = false) { return Exception(msg, code, remote_); } /// These creators are for messages that were received by network or generated by a third-party library in runtime. /// Please use a constructor for all other cases. static Exception createRuntime(int code, const String & message) { return Exception(message, code); } static Exception createRuntime(int code, String & message) { return Exception(message, code); } static Exception createRuntime(int code, String && message) { return Exception(std::move(message), code); } // Format message with fmt::format, like the logging functions. template Exception(int code, FormatStringHelper fmt, Args &&... args) : Exception(fmt.format(std::forward(args)...), code) {} struct CreateFromPocoTag {}; struct CreateFromSTDTag {}; Exception(CreateFromPocoTag, const Poco::Exception & exc); Exception(CreateFromSTDTag, const std::exception & exc); Exception * clone() const override { return new Exception(*this); } void rethrow() const override { throw *this; } // NOLINT const char * name() const noexcept override { return "DB::Exception"; } const char * what() const noexcept override { return message().data(); } /// Add something to the existing message. template void addMessage(fmt::format_string format, Args &&... args) { addMessage(fmt::format(format, std::forward(args)...)); } void addMessage(const std::string& message) { addMessage(MessageMasked(message)); } void addMessage(const MessageMasked & msg_masked) { extendedMessage(msg_masked.msg); } /// Used to distinguish local exceptions from the one that was received from remote node. void setRemoteException(bool remote_ = true) { remote = remote_; } bool isRemoteException() const { return remote; } std::string getStackTraceString() const; /// Used for system.errors FramePointers getStackFramePointers() const; std::string_view tryGetMessageFormatString() const { return message_format_string; } std::vector getMessageFormatStringArgs() const { return message_format_string_args; } private: #ifndef STD_EXCEPTION_HAS_STACK_TRACE StackTrace trace; #endif bool remote = false; const char * className() const noexcept override { return "DB::Exception"; } protected: std::string_view message_format_string; std::vector message_format_string_args; /// Local copy of static per-thread thread_frame_pointers, should be mutable to be unpoisoned on printout mutable std::vector capture_thread_frame_pointers; }; [[noreturn]] void abortOnFailedAssertion(const String & description, void * const * trace, size_t trace_offset, size_t trace_size); [[noreturn]] void abortOnFailedAssertion(const String & description); std::string getExceptionStackTraceString(const std::exception & e); std::string getExceptionStackTraceString(std::exception_ptr e); /// Contains an additional member `saved_errno` class ErrnoException : public Exception { public: ErrnoException(std::string && msg, int code, int with_errno) : Exception(msg, code), saved_errno(with_errno) { capture_thread_frame_pointers = getThreadFramePointers(); addMessage(", {}", errnoToString(saved_errno)); } /// Message must be a compile-time constant template requires std::is_convertible_v ErrnoException(int code, T && message) : Exception(message, code), saved_errno(errno) { capture_thread_frame_pointers = getThreadFramePointers(); addMessage(", {}", errnoToString(saved_errno)); } // Format message with fmt::format, like the logging functions. template ErrnoException(int code, FormatStringHelper fmt, Args &&... args) : Exception(fmt.format(std::forward(args)...), code), saved_errno(errno) { addMessage(", {}", errnoToString(saved_errno)); } template ErrnoException(int code, int with_errno, FormatStringHelper fmt, Args &&... args) : Exception(fmt.format(std::forward(args)...), code), saved_errno(with_errno) { addMessage(", {}", errnoToString(saved_errno)); } template [[noreturn]] static void throwWithErrno(int code, int with_errno, FormatStringHelper fmt, Args &&... args) { auto e = ErrnoException(code, with_errno, std::move(fmt), std::forward(args)...); throw e; /// NOLINT } template [[noreturn]] static void throwFromPath(int code, const std::string & path, FormatStringHelper fmt, Args &&... args) { auto e = ErrnoException(code, errno, std::move(fmt), std::forward(args)...); e.path = path; throw e; /// NOLINT } template [[noreturn]] static void throwFromPathWithErrno(int code, const std::string & path, int with_errno, FormatStringHelper fmt, Args &&... args) { auto e = ErrnoException(code, with_errno, std::move(fmt), std::forward(args)...); e.path = path; throw e; /// NOLINT } ErrnoException * clone() const override { return new ErrnoException(*this); } void rethrow() const override { throw *this; } // NOLINT int getErrno() const { return saved_errno; } std::optional getPath() const { return path; } private: int saved_errno; std::optional path; const char * name() const noexcept override { return "DB::ErrnoException"; } const char * className() const noexcept override { return "DB::ErrnoException"; } }; /// An exception to use in unit tests to test interfaces. /// It is distinguished from others, so it does not have to be logged. class TestException : public Exception { public: using Exception::Exception; }; using Exceptions = std::vector; /** Try to write an exception to the log (and forget about it). * Can be used in destructors in the catch-all block. */ /// TODO: Logger leak constexpr overload void tryLogCurrentException(const char * log_name, const std::string & start_of_message = "", LogsLevel level = LogsLevel::error); void tryLogCurrentException(Poco::Logger * logger, const std::string & start_of_message = "", LogsLevel level = LogsLevel::error); void tryLogCurrentException(LoggerPtr logger, const std::string & start_of_message = "", LogsLevel level = LogsLevel::error); void tryLogCurrentException(const AtomicLogger & logger, const std::string & start_of_message = "", LogsLevel level = LogsLevel::error); /** Prints current exception in canonical format. * with_stacktrace - prints stack trace for DB::Exception. * check_embedded_stacktrace - if DB::Exception has embedded stacktrace then * only this stack trace will be printed. * with_extra_info - add information about the filesystem in case of "No space left on device" and similar. */ std::string getCurrentExceptionMessage(bool with_stacktrace, bool check_embedded_stacktrace = false, bool with_extra_info = true); PreformattedMessage getCurrentExceptionMessageAndPattern(bool with_stacktrace, bool check_embedded_stacktrace = false, bool with_extra_info = true); /// Returns error code from ErrorCodes int getCurrentExceptionCode(); int getExceptionErrorCode(std::exception_ptr e); /// Returns string containing extra diagnostic info for specific exceptions (like "no space left on device" and "memory limit exceeded") std::string getExtraExceptionInfo(const std::exception & e); /// An execution status of any piece of code, contains return code and optional error struct ExecutionStatus { int code = 0; std::string message; ExecutionStatus() = default; explicit ExecutionStatus(int return_code, const std::string & exception_message = "") : code(return_code), message(exception_message) {} static ExecutionStatus fromCurrentException(const std::string & start_of_message = "", bool with_stacktrace = false); static ExecutionStatus fromText(const std::string & data); std::string serializeText() const; void deserializeText(const std::string & data); bool tryDeserializeText(const std::string & data); }; /// TODO: Logger leak constexpr overload void tryLogException(std::exception_ptr e, const char * log_name, const std::string & start_of_message = ""); void tryLogException(std::exception_ptr e, LoggerPtr logger, const std::string & start_of_message = ""); void tryLogException(std::exception_ptr e, const AtomicLogger & logger, const std::string & start_of_message = ""); std::string getExceptionMessage(const Exception & e, bool with_stacktrace, bool check_embedded_stacktrace = false); PreformattedMessage getExceptionMessageAndPattern(const Exception & e, bool with_stacktrace, bool check_embedded_stacktrace = false); std::string getExceptionMessage(std::exception_ptr e, bool with_stacktrace, bool check_embedded_stacktrace = false); template requires std::is_pointer_v T exception_cast(std::exception_ptr e) { try { std::rethrow_exception(e); } catch (std::remove_pointer_t & concrete) { return &concrete; } catch (...) { return nullptr; } } }