/************************************************************************* * * Copyright 2016 Realm Inc. * * Licensed 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 REALM_EXCEPTIONS_HPP #define REALM_EXCEPTIONS_HPP #include #include #include #include namespace realm { using util::ExceptionWithBacktrace; /// Thrown by various functions to indicate that a specified table does not /// exist. class NoSuchTable : public ExceptionWithBacktrace { public: const char* message() const noexcept override; }; class InvalidTableRef : public ExceptionWithBacktrace { public: InvalidTableRef(const char* cause) : m_message(cause) { } const char* message() const noexcept override { return m_message.c_str(); } std::string m_message; }; /// Thrown by various functions to indicate that a specified table name is /// already in use. class TableNameInUse : public ExceptionWithBacktrace { public: const char* message() const noexcept override; }; // Thrown by functions that require a table to **not** be the target of link // columns, unless those link columns are part of the table itself. class CrossTableLinkTarget : public ExceptionWithBacktrace { public: const char* message() const noexcept override; }; /// Thrown by various functions to indicate that the dynamic type of a table /// does not match a particular other table type (dynamic or static). class DescriptorMismatch : public ExceptionWithBacktrace { public: const char* message() const noexcept override; }; /// The UnsupportedFileFormatVersion exception is thrown by DB::open() /// constructor when opening a database that uses a deprecated file format /// and/or a deprecated history schema which this version of Realm cannot /// upgrade from. class UnsupportedFileFormatVersion : public ExceptionWithBacktrace<> { public: UnsupportedFileFormatVersion(int source_version); /// The unsupported version of the file. int source_version = 0; }; /// Thrown when a sync agent attempts to join a session in which there is /// already a sync agent. A session may only contain one sync agent at any given /// time. class MultipleSyncAgents : public ExceptionWithBacktrace { public: const char* message() const noexcept override; }; /// Thrown when memory can no longer be mapped to. When mmap/remap fails. class AddressSpaceExhausted : public std::runtime_error { public: AddressSpaceExhausted(const std::string& msg); /// runtime_error::what() returns the msg provided in the constructor. }; /// Thrown when creating references that are too large to be contained in our ref_type (size_t) class MaximumFileSizeExceeded : public std::runtime_error { public: MaximumFileSizeExceeded(const std::string& msg); /// runtime_error::what() returns the msg provided in the constructor. }; /// Thrown when writing fails because the disk is full. class OutOfDiskSpace : public std::runtime_error { public: OutOfDiskSpace(const std::string& msg); /// runtime_error::what() returns the msg provided in the constructor. }; /// Thrown when a key can not by found class KeyNotFound : public std::runtime_error { public: KeyNotFound(const std::string& msg) : std::runtime_error(msg) { } }; /// Thrown when a column can not by found class ColumnNotFound : public std::runtime_error { public: ColumnNotFound() : std::runtime_error("Column not found") { } }; /// Thrown when a column key is already used class ColumnAlreadyExists : public std::runtime_error { public: ColumnAlreadyExists() : std::runtime_error("Column already exists") { } }; /// Thrown when a key is already existing when trying to create a new object class KeyAlreadyUsed : public std::runtime_error { public: KeyAlreadyUsed(const std::string& msg) : std::runtime_error(msg) { } }; // SerialisationError intentionally does not inherit ExceptionWithBacktrace // because the query-based-sync permissions queries generated on the server // use a LinksToNode which is not currently serialisable (this limitation can // be lifted in core 6 given stable ids). Coupled with query metrics which // serialize all queries, the capturing of the stack for these frequent // permission queries shows up in performance profiles. class SerialisationError : public std::runtime_error { public: SerialisationError(const std::string& msg); /// runtime_error::what() returns the msg provided in the constructor. }; // thrown when a user constructed link path is not a valid input class InvalidPathError : public std::runtime_error { public: InvalidPathError(const std::string& msg); /// runtime_error::what() returns the msg provided in the constructor. }; class DuplicatePrimaryKeyValueException : public std::logic_error { public: DuplicatePrimaryKeyValueException(std::string object_type, std::string property); std::string const& object_type() const { return m_object_type; } std::string const& property() const { return m_property; } private: std::string m_object_type; std::string m_property; }; /// The \c LogicError exception class is intended to be thrown only when /// applications (or bindings) violate rules that are stated (or ought to have /// been stated) in the documentation of the public API, and only in cases /// where the violation could have been easily and efficiently predicted by the /// application. In other words, this exception class is for the cases where /// the error is due to incorrect use of the public API. /// /// This class is not supposed to be caught by applications. It is not even /// supposed to be considered part of the public API, and therefore the /// documentation of the public API should **not** mention the \c LogicError /// exception class by name. Note how this contrasts with other exception /// classes, such as \c NoSuchTable, which are part of the public API, and are /// supposed to be mentioned in the documentation by name. The \c LogicError /// exception is part of Realm's private API. /// /// In other words, the \c LogicError class should exclusively be used in /// replacement (or in addition to) asserts (debug or not) in order to /// guarantee program interruption, while still allowing for complete /// test-cases to be written and run. /// /// To this effect, the special `CHECK_LOGIC_ERROR()` macro is provided as a /// test framework plugin to allow unit tests to check that the functions in /// the public API do throw \c LogicError when rules are violated. /// /// The reason behind hiding this class from the public API is to prevent users /// from getting used to the idea that "Undefined Behaviour" equates a specific /// exception being thrown. The whole point of properly documenting "Undefined /// Behaviour" cases is to help the user know what the limits are, without /// constraining the database to handle every and any use-case thrown at it. class LogicError : public ExceptionWithBacktrace { public: enum ErrorKind { string_too_big, binary_too_big, table_name_too_long, column_name_too_long, column_name_in_use, invalid_column_name, table_index_out_of_range, row_index_out_of_range, column_index_out_of_range, string_position_out_of_range, link_index_out_of_range, bad_version, illegal_type, /// Indicates that an argument has a value that is illegal in combination /// with another argument, or with the state of an involved object. illegal_combination, /// Indicates a data type mismatch, such as when `Table::find_pkey_int()` is /// called and the type of the primary key is not `type_Int`. type_mismatch, /// Indicates that two involved tables are not in the same group. group_mismatch, /// Indicates that an involved descriptor is of the wrong kind, i.e., if /// it is a subtable descriptor, and the function requires a root table /// descriptor. wrong_kind_of_descriptor, /// Indicates that an involved table is of the wrong kind, i.e., if it /// is a subtable, and the function requires a root table, or if it is a /// free-standing table, and the function requires a group-level table. wrong_kind_of_table, /// Indicates that an involved accessor is was detached, i.e., was not /// attached to an underlying object. detached_accessor, /// Indicates that a specified row index of a target table (a link) is /// out of range. This is used for disambiguation in cases such as /// Table::set_link() where one specifies both a row index of the origin /// table, and a row index of the target table. target_row_index_out_of_range, // Indicates that an involved column lacks a search index. no_search_index, /// Indicates that a modification was attempted that would have produced a /// duplicate primary value. unique_constraint_violation, /// User attempted to insert null in non-nullable column column_not_nullable, /// Group::open() is called on a group accessor that is already in the /// attached state. Or Group::open() or Group::commit() is called on a /// group accessor that is managed by a DB object. wrong_group_state, /// No active transaction on a particular Transaction object (e.g. after commit) /// or the Transaction object is of the wrong type (write to a read-only transaction) wrong_transact_state, /// Attempted use of a continuous transaction through a DB /// object with no history. See Replication::get_history(). no_history, /// Durability setting (as passed to the DB constructor) was /// not consistent across the session. mixed_durability, /// History type (as specified by the Replication implementation passed /// to the DB constructor) was not consistent across the /// session. mixed_history_type, /// History schema version (as specified by the Replication /// implementation passed to the DB constructor) was not /// consistent across the session. mixed_history_schema_version, /// Adding rows to a table with no columns is not supported. table_has_no_columns, /// Referring to a column that has been deleted. column_does_not_exist, /// You can not add index on a subtable of a subtable subtable_of_subtable_index, /// You try to instantiate a collection object not matching column type collection_type_mismatch }; LogicError(ErrorKind message); const char* message() const noexcept override; ErrorKind kind() const noexcept; private: ErrorKind m_kind; }; // Implementation: // LCOV_EXCL_START (Wording of what() strings are not to be tested) inline const char* NoSuchTable::message() const noexcept { return "No such table exists"; } inline const char* TableNameInUse::message() const noexcept { return "The specified table name is already in use"; } inline const char* CrossTableLinkTarget::message() const noexcept { return "Table is target of cross-table link columns"; } inline const char* DescriptorMismatch::message() const noexcept { return "Table descriptor mismatch"; } inline UnsupportedFileFormatVersion::UnsupportedFileFormatVersion(int version) : ExceptionWithBacktrace<>( util::format("Database has an unsupported version (%1) and cannot be upgraded", version)) , source_version(version) { } inline const char* MultipleSyncAgents::message() const noexcept { return "Multiple sync agents attempted to join the same session"; } // LCOV_EXCL_STOP inline AddressSpaceExhausted::AddressSpaceExhausted(const std::string& msg) : std::runtime_error(msg) { } inline MaximumFileSizeExceeded::MaximumFileSizeExceeded(const std::string& msg) : std::runtime_error(msg) { } inline OutOfDiskSpace::OutOfDiskSpace(const std::string& msg) : std::runtime_error(msg) { } inline SerialisationError::SerialisationError(const std::string& msg) : std::runtime_error(msg) { } inline InvalidPathError::InvalidPathError(const std::string& msg) : runtime_error(msg) { } inline LogicError::LogicError(LogicError::ErrorKind k) : m_kind(k) { } inline LogicError::ErrorKind LogicError::kind() const noexcept { return m_kind; } } // namespace realm #endif // REALM_EXCEPTIONS_HPP