/************************************************************************* * * 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_OBJ_HPP #define REALM_OBJ_HPP #include #include #include #include #include #define REALM_CLUSTER_IF namespace realm { class TableClusterTree; class Replication; class TableView; class CollectionBase; class CascadeState; class LstBase; class SetBase; class ObjList; struct GlobalKey; template class Lst; template class Set; template using LstPtr = std::unique_ptr>; using LstBasePtr = std::unique_ptr; template using SetPtr = std::unique_ptr>; using SetBasePtr = std::unique_ptr; using CollectionBasePtr = std::unique_ptr; using LinkCollectionPtr = std::unique_ptr; class LnkLst; using LnkLstPtr = std::unique_ptr; class LnkSet; using LnkSetPtr = std::unique_ptr; template class Set; class Dictionary; class DictionaryLinkValues; using DictionaryPtr = std::unique_ptr; namespace _impl { class DeepChangeChecker; } enum JSONOutputMode { output_mode_json, // default / existing implementation for outputting realm to json output_mode_xjson, // extended json as described in the spec output_mode_xjson_plus, // extended json as described in the spec with additional modifier used for sync }; /// The status of an accessor after a call to `update_if_needed()`. enum class UpdateStatus { /// The owning object or column no longer exist, and the accessor could /// not be updated. The accessor should be left in a detached state /// after this, and further calls to `update_if_needed()` are not /// guaranteed to reattach the accessor. Detached, /// The underlying data of the accessor was changed since the last call /// to `update_if_needed()`. The accessor is still valid. Updated, /// The underlying data of the accessor did not change since the last /// call to `update_if_needed()`, and the accessor is still valid in its /// current state. NoChange, }; // 'Object' would have been a better name, but it clashes with a class in ObjectStore class Obj { public: Obj() : m_table(nullptr) , m_row_ndx(size_t(-1)) , m_storage_version(-1) , m_valid(false) { } Obj(TableRef table, MemRef mem, ObjKey key, size_t row_ndx); TableRef get_table() const noexcept { return m_table.cast_away_const(); } Allocator& get_alloc() const; bool operator==(const Obj& other) const; ObjKey get_key() const noexcept { return m_key; } GlobalKey get_object_id() const; ObjLink get_link() const; Replication* get_replication() const; // Check if this object is default constructed explicit operator bool() const noexcept { return m_table != nullptr; } /// Check if the object is still alive bool is_valid() const noexcept; /// Will throw if object is not valid void check_valid() const; /// Delete object from table. Object is invalid afterwards. void remove(); /// Invalidate /// - this turns the object into a tombstone if links to the object exist. /// - deletes the object is no links to the object exist. /// - To be used by the Sync client. void invalidate(); template U get(ColKey col_key) const; Mixed get_any(ColKey col_key) const; Mixed get_any(StringData col_name) const { return get_any(get_column_key(col_name)); } Mixed get_any(std::vector::iterator path_start, std::vector::iterator path_end) const; Mixed get_primary_key() const; template U get(StringData col_name) const { return get(get_column_key(col_name)); } bool is_unresolved(ColKey col_key) const; int cmp(const Obj& other, ColKey col_key) const; size_t get_link_count(ColKey col_key) const; bool is_null(ColKey col_key) const; bool is_null(StringData col_name) const { return is_null(get_column_key(col_name)); } bool has_backlinks(bool only_strong_links) const; size_t get_backlink_count() const; size_t get_backlink_count(const Table& origin, ColKey origin_col_key) const; ObjKey get_backlink(const Table& origin, ColKey origin_col_key, size_t backlink_ndx) const; TableView get_backlink_view(TableRef src_table, ColKey src_col_key); // To be used by the query system when a single object should // be tested. Will allow a function to be called in the context // of the owning cluster. template bool evaluate(T func) const; void to_json(std::ostream& out, size_t link_depth, const std::map& renames, std::vector& followed, JSONOutputMode output_mode) const; void to_json(std::ostream& out, size_t link_depth, const std::map& renames, JSONOutputMode output_mode = output_mode_json) const { std::vector followed; to_json(out, link_depth, renames, followed, output_mode); } std::string to_string() const; // Get the path in a minimal format without including object accessors. // If you need to obtain additional information for each object in the path, // you should use get_fat_path() or traverse_path() instead (see below). struct PathElement; struct Path { TableKey top_table; ObjKey top_objkey; std::vector path_from_top; }; Path get_path() const; // Get the fat path to this object expressed as a vector of fat path elements. // each Fat path elements include a Obj allowing for low cost access to the // objects data. // For a top-level object, the returned vector will be empty. // For an embedded object, the vector has the top object as first element, // and the embedded object itself is not included in the path. struct FatPathElement; using FatPath = std::vector; FatPath get_fat_path() const; // For an embedded object, traverse the path leading to this object. // The PathSizer is called first to set the size of the path // Then there is one call for each object on that path, starting with the top level object // The embedded object itself is not considered part of the path. // Note: You should never provide the path_index for calls to traverse_path. using Visitor = std::function; using PathSizer = std::function; void traverse_path(Visitor v, PathSizer ps, size_t path_index = 0) const; template Obj& set(ColKey col_key, U value, bool is_default = false); // Create a new object and link it. If an embedded object // is already set, it will be removed. If a non-embedded // object is already set, we throw LogicError (to prevent // dangling objects, since they do not delete automatically // if they are not embedded...) Obj create_and_set_linked_object(ColKey col_key, bool is_default = false); // Clear all fields of a linked object returning it to its // default state. If the object does not exist, create a // new object and link it. (To Be Implemented) Obj clear_linked_object(ColKey col_key); Obj& set_any(ColKey col_key, Mixed value, bool is_default = false); template Obj& set(StringData col_name, U value, bool is_default = false) { return set(get_column_key(col_name), value, is_default); } Obj& set_null(ColKey col_key, bool is_default = false); Obj& set_null(StringData col_name, bool is_default = false) { return set_null(get_column_key(col_name), is_default); } Obj& add_int(ColKey col_key, int64_t value); Obj& add_int(StringData col_name, int64_t value) { return add_int(get_column_key(col_name), value); } template Obj& set_list_values(ColKey col_key, const std::vector& values); template std::vector get_list_values(ColKey col_key) const; template Obj& set_all(Head v, Tail... tail); void assign(const Obj& other); Obj get_linked_object(ColKey link_col_key) const; Obj get_linked_object(StringData link_col_name) const; template Lst get_list(ColKey col_key) const; template LstPtr get_list_ptr(ColKey col_key) const; template Lst get_list(StringData col_name) const { return get_list(get_column_key(col_name)); } LnkLst get_linklist(ColKey col_key) const; LnkLstPtr get_linklist_ptr(ColKey col_key) const; LnkLst get_linklist(StringData col_name) const; /// Get a type-erased list instance for the given list column. /// /// Note: For lists of links, this always returns a `LnkLst`, rather than a /// `Lst`. Use `get_list_ptr(col_key)` to get a list of /// links with uncondensed indices. LstBasePtr get_listbase_ptr(ColKey col_key) const; template Set get_set(StringData col_name) const { return get_set(get_column_key(col_name)); } template Set get_set(ColKey col_key) const; template SetPtr get_set_ptr(ColKey col_key) const; LnkSet get_linkset(ColKey col_key) const; LnkSetPtr get_linkset_ptr(ColKey col_key) const; SetBasePtr get_setbase_ptr(ColKey col_key) const; Dictionary get_dictionary(ColKey col_key) const; DictionaryPtr get_dictionary_ptr(ColKey col_key) const; Dictionary get_dictionary(StringData col_name) const; CollectionBasePtr get_collection_ptr(ColKey col_key) const; LinkCollectionPtr get_linkcollection_ptr(ColKey col_key) const; void assign_pk_and_backlinks(const Obj& other); class Internal { friend class _impl::DeepChangeChecker; static ref_type get_ref(const Obj& obj, ColKey col_key); }; private: friend class ArrayBacklink; friend class CascadeState; friend class Cluster; friend class ColumnListBase; friend class CollectionBase; friend class TableView; template friend class Collection; template friend class CollectionBaseImpl; template friend class Lst; friend class LnkLst; friend class Dictionary; friend class LinkMap; template friend class Set; friend class Table; friend class Transaction; mutable TableRef m_table; ObjKey m_key; mutable MemRef m_mem; mutable size_t m_row_ndx; mutable uint64_t m_storage_version; mutable bool m_valid; Allocator& _get_alloc() const noexcept; /// Update the accessor. Returns true when the accessor was updated to /// reflect new changes to the underlying state. bool update() const; // update if needed - with and without check of table instance version: bool update_if_needed() const; bool _update_if_needed() const; // no check, use only when already checked /// Update the accessor (and return `UpdateStatus::Detached` if the Obj is /// no longer valid, rather than throwing an exception). UpdateStatus update_if_needed_with_status() const; template bool do_is_null(ColKey::Idx col_ndx) const; const TableClusterTree* get_tree_top() const; ColKey get_column_key(StringData col_name) const; ColKey get_primary_key_column() const; TableKey get_table_key() const; TableRef get_target_table(ColKey col_key) const; TableRef get_target_table(ObjLink link) const; const Spec& get_spec() const; template U _get(ColKey::Idx col_ndx) const; template int cmp(const Obj& other, ColKey::Idx col_ndx) const; int cmp(const Obj& other, ColKey::Idx col_ndx) const; ObjKey get_backlink(ColKey backlink_col, size_t backlink_ndx) const; // Return all backlinks from a specific backlink column std::vector get_all_backlinks(ColKey backlink_col) const; // Return number of backlinks from a specific backlink column size_t get_backlink_cnt(ColKey backlink_col) const; ObjKey get_unfiltered_link(ColKey col_key) const; template Obj& _set(size_t col_ndx, Val v); template Obj& _set(size_t col_ndx, Head v, Tail... tail); ColKey spec_ndx2colkey(size_t col_ndx); size_t colkey2spec_ndx(ColKey); bool ensure_writeable(); void sync(Array& arr); int_fast64_t bump_content_version(); void bump_both_versions(); template void do_set_null(ColKey col_key); // Dictionary support size_t get_row_ndx() const { return m_row_ndx; } void set_int(ColKey col_key, int64_t value); void add_backlink(ColKey backlink_col, ObjKey origin_key); bool remove_one_backlink(ColKey backlink_col, ObjKey origin_key); void nullify_link(ColKey origin_col, ObjLink target_key); // Used when inserting a new link. You will not remove existing links in this process void set_backlink(ColKey col_key, ObjLink new_link) const; // Used when replacing a link, return true if CascadeState contains objects to remove bool replace_backlink(ColKey col_key, ObjLink old_link, ObjLink new_link, CascadeState& state) const; // Used when removing a backlink, return true if CascadeState contains objects to remove bool remove_backlink(ColKey col_key, ObjLink old_link, CascadeState& state) const; template inline void set_spec(T&, ColKey); }; std::ostream& operator<<(std::ostream&, const Obj& obj); template <> int64_t Obj::_get(ColKey::Idx col_ndx) const; struct Obj::FatPathElement { Obj obj; // Object which embeds... ColKey col_key; // Column holding link or link list which embeds... Mixed index; // index into link list or dictionary (or null) }; struct Obj::PathElement { ColKey col_key; // Column holding link or link list which embeds... Mixed index; // index into link list or dictionary (or null) }; template <> Obj& Obj::set(ColKey, int64_t value, bool is_default); template <> Obj& Obj::set(ColKey, ObjKey value, bool is_default); template <> Obj& Obj::set(ColKey, ObjLink value, bool is_default); template <> inline Obj& Obj::set(ColKey col_key, int value, bool is_default) { return set(col_key, int_fast64_t(value), is_default); } template <> inline Obj& Obj::set(ColKey col_key, uint_fast64_t value, bool is_default) { int_fast64_t value_2 = 0; if (REALM_UNLIKELY(util::int_cast_with_overflow_detect(value, value_2))) { REALM_TERMINATE("Unsigned integer too big."); } return set(col_key, value_2, is_default); } template <> inline Obj& Obj::set(ColKey col_key, const char* str, bool is_default) { return set(col_key, StringData(str), is_default); } template <> inline Obj& Obj::set(ColKey col_key, char* str, bool is_default) { return set(col_key, StringData(str), is_default); } template <> inline Obj& Obj::set(ColKey col_key, std::string str, bool is_default) { return set(col_key, StringData(str), is_default); } template <> inline Obj& Obj::set(ColKey col_key, realm::null, bool is_default) { return set_null(col_key, is_default); } template <> inline Obj& Obj::set(ColKey col_key, util::Optional value, bool is_default) { return value ? set(col_key, *value, is_default) : set_null(col_key, is_default); } template <> inline Obj& Obj::set(ColKey col_key, util::Optional value, bool is_default) { return value ? set(col_key, *value, is_default) : set_null(col_key, is_default); } template <> inline Obj& Obj::set(ColKey col_key, util::Optional value, bool is_default) { return value ? set(col_key, *value, is_default) : set_null(col_key, is_default); } template <> inline Obj& Obj::set(ColKey col_key, util::Optional value, bool is_default) { return value ? set(col_key, *value, is_default) : set_null(col_key, is_default); } template <> inline Obj& Obj::set(ColKey col_key, util::Optional value, bool is_default) { return value ? set(col_key, *value, is_default) : set_null(col_key, is_default); } template <> inline Obj& Obj::set(ColKey col_key, util::Optional value, bool is_default) { return value ? set(col_key, *value, is_default) : set_null(col_key, is_default); } template Obj& Obj::set_list_values(ColKey col_key, const std::vector& values) { size_t sz = values.size(); auto list = get_list(col_key); size_t list_sz = list.size(); if (sz < list_sz) { list.resize(sz); list_sz = sz; } size_t i = 0; while (i < list_sz) { list.set(i, values[i]); i++; } while (i < sz) { list.add(values[i]); i++; } return *this; } template std::vector Obj::get_list_values(ColKey col_key) const { std::vector values; auto list = get_list(col_key); for (auto v : list) values.push_back(v); return values; } inline Obj Obj::get_linked_object(StringData link_col_name) const { ColKey col = get_column_key(link_col_name); return get_linked_object(col); } template inline Obj& Obj::_set(size_t col_ndx, Val v) { return set(spec_ndx2colkey(col_ndx), v); } template inline Obj& Obj::_set(size_t col_ndx, Head v, Tail... tail) { set(spec_ndx2colkey(col_ndx), v); return _set(col_ndx + 1, tail...); } template inline Obj& Obj::set_all(Head v, Tail... tail) { size_t start_index = 0; // Avoid trying to set the PK column. if (get_primary_key_column()) { REALM_ASSERT(colkey2spec_ndx(get_primary_key_column()) == 0); start_index = 1; } return _set(start_index, v, tail...); } inline bool Obj::update_if_needed() const { auto current_version = get_alloc().get_storage_version(); if (current_version != m_storage_version) { return update(); } return false; } inline int_fast64_t Obj::bump_content_version() { Allocator& alloc = get_alloc(); return alloc.bump_content_version(); } inline void Obj::bump_both_versions() { Allocator& alloc = get_alloc(); alloc.bump_content_version(); alloc.bump_storage_version(); } } // namespace realm #endif // REALM_OBJ_HPP