From 0634e94968410e1895b6c9a061de0c2025acb30a Mon Sep 17 00:00:00 2001 From: James Stone Date: Mon, 29 Aug 2022 17:35:03 -0700 Subject: [PATCH 1/4] refactor link tracing code --- src/realm/list.cpp | 2 + src/realm/obj.cpp | 467 ++++++++++++++++++------------- src/realm/obj.hpp | 2 + src/realm/table.cpp | 95 ++++++- src/realm/table.hpp | 19 ++ test/object-store/migrations.cpp | 100 +++++++ 6 files changed, 491 insertions(+), 194 deletions(-) diff --git a/src/realm/list.cpp b/src/realm/list.cpp index b2b40d909ef..087692033e4 100644 --- a/src/realm/list.cpp +++ b/src/realm/list.cpp @@ -280,6 +280,8 @@ void Lst::do_clear() target_obj.remove_one_backlink(backlink_col, m_obj.get_key()); // Throws size_t num_remaining = target_obj.get_backlink_count(*origin_table, m_col_key); if (num_remaining == 0) { + // embedded objects should only have one incoming link + REALM_ASSERT_EX(target_obj.get_backlink_count() == 0, target_obj.get_backlink_count()); state.m_to_be_deleted.emplace_back(target_table_key, target_key); } } diff --git a/src/realm/obj.cpp b/src/realm/obj.cpp index 517e31beb9e..0891c980ef8 100644 --- a/src/realm/obj.cpp +++ b/src/realm/obj.cpp @@ -696,6 +696,61 @@ size_t Obj::get_backlink_cnt(ColKey backlink_col) const void Obj::traverse_path(Visitor v, PathSizer ps, size_t path_length) const { + struct BacklinkTraverser : public LinkTranslator { + BacklinkTraverser(Obj origin, ColKey origin_col_key, Obj dest) + : LinkTranslator(origin, origin_col_key) + , m_dest_obj(dest) + { + } + void on_list_of_links(LnkLst ll) final + { + auto i = ll.find_first(m_dest_obj.get_key()); + REALM_ASSERT(i != realm::npos); + m_index = Mixed(int64_t(i)); + } + void on_dictionary(Dictionary dict) final + { + for (auto it : dict) { + if (it.second.is_type(type_TypedLink) && it.second.get_link() == m_dest_obj.get_link()) { + m_index = it.first; + break; + } + } + REALM_ASSERT(!m_index.is_null()); + } + void on_list_of_mixed(Lst) final + { + REALM_UNREACHABLE(); // we don't support Mixed link to embedded object yet + } + void on_list_of_typedlink(Lst) final + { + REALM_UNREACHABLE(); // we don't support TypedLink to embedded object yet + } + void on_set_of_links(LnkSet) final + { + REALM_UNREACHABLE(); // sets of embedded objects are not allowed at the schema level + } + void on_set_of_mixed(Set) final + { + REALM_UNREACHABLE(); // we don't support Mixed link to embedded object yet + } + void on_set_of_typedlink(Set) final + { + REALM_UNREACHABLE(); // we don't support TypedLink to embedded object yet + } + void on_link_property(ColKey) final {} + void on_mixed_property(ColKey) final {} + void on_typedlink_property(ColKey) final {} + Mixed result() + { + return m_index; + } + + private: + Mixed m_index; + Obj m_dest_obj; + }; + if (m_table->is_embedded()) { REALM_ASSERT(get_backlink_count() == 1); m_table->for_each_backlink_column([&](ColKey col_key) { @@ -704,23 +759,9 @@ void Obj::traverse_path(Visitor v, PathSizer ps, size_t path_length) const TableRef tr = m_table->get_opposite_table(col_key); Obj obj = tr->get_object(backlinks[0]); // always the first (and only) auto next_col_key = m_table->get_opposite_column(col_key); - Mixed index; - if (next_col_key.get_attrs().test(col_attr_List)) { - auto ll = obj.get_linklist(next_col_key); - auto i = ll.find_first(get_key()); - REALM_ASSERT(i != realm::npos); - index = Mixed(int64_t(i)); - } - else if (next_col_key.get_attrs().test(col_attr_Dictionary)) { - auto dict = obj.get_dictionary(next_col_key); - for (auto it : dict) { - if (it.second.is_type(type_TypedLink) && it.second.get_link() == get_link()) { - index = it.first; - break; - } - } - REALM_ASSERT(!index.is_null()); - } + BacklinkTraverser traverser{obj, next_col_key, *this}; + traverser.run(); + Mixed index = traverser.result(); obj.traverse_path(v, ps, path_length + 1); v(obj, next_col_key, index); return true; // early out @@ -1778,92 +1819,88 @@ inline void nullify_set(Obj& obj, ColKey origin_col_key, T target) } } // namespace +template +inline void Obj::nullify_single_link(ColKey col, ValueType target) +{ + ColKey::Idx origin_col_ndx = col.get_index(); + Allocator& alloc = get_alloc(); + Array fallback(alloc); + Array& fields = get_tree_top()->get_fields_accessor(fallback, m_mem); + ArrayType links(alloc); + links.set_parent(&fields, origin_col_ndx.val + 1); + links.init_from_parent(); + // Ensure we are nullifying correct link + REALM_ASSERT(links.get(m_row_ndx) == target); + links.set(m_row_ndx, ValueType{}); + sync(fields); + + if (Replication* repl = get_replication()) + repl->nullify_link(m_table.unchecked_ptr(), col, + m_key); // Throws +} + void Obj::nullify_link(ColKey origin_col_key, ObjLink target_link) && { REALM_ASSERT(get_alloc().get_storage_version() == m_storage_version); - ColKey::Idx origin_col_ndx = origin_col_key.get_index(); - Allocator& alloc = get_alloc(); - - ColumnAttrMask attr = origin_col_key.get_attrs(); - if (attr.test(col_attr_List)) { - if (origin_col_key.get_type() == col_type_LinkList) { - nullify_linklist(*this, origin_col_key, target_link.get_obj_key()); + struct LinkNullifier : public LinkTranslator { + LinkNullifier(Obj origin_obj, ColKey origin_col, ObjLink target) + : LinkTranslator(origin_obj, origin_col) + , m_target_link(target) + { } - else if (origin_col_key.get_type() == col_type_TypedLink) { - nullify_linklist(*this, origin_col_key, target_link); + void on_list_of_links(LnkLst) final + { + nullify_linklist(m_origin_obj, m_origin_col_key, m_target_link.get_obj_key()); } - else if (origin_col_key.get_type() == col_type_Mixed) { - nullify_linklist(*this, origin_col_key, Mixed(target_link)); + void on_list_of_mixed(Lst) final + { + nullify_linklist(m_origin_obj, m_origin_col_key, Mixed(m_target_link)); } - else { - REALM_ASSERT(false); + void on_list_of_typedlink(Lst) final + { + nullify_linklist(m_origin_obj, m_origin_col_key, m_target_link); } - } - else if (attr.test(col_attr_Set)) { - if (origin_col_key.get_type() == col_type_Link) { - nullify_set(*this, origin_col_key, target_link.get_obj_key()); + void on_set_of_links(LnkSet) final + { + nullify_set(m_origin_obj, m_origin_col_key, m_target_link.get_obj_key()); } - else if (origin_col_key.get_type() == col_type_TypedLink) { - nullify_set(*this, origin_col_key, target_link); + void on_set_of_mixed(Set) final + { + nullify_set(m_origin_obj, m_origin_col_key, Mixed(m_target_link)); } - else if (origin_col_key.get_type() == col_type_Mixed) { - nullify_set(*this, origin_col_key, Mixed(target_link)); + void on_set_of_typedlink(Set) final + { + nullify_set(m_origin_obj, m_origin_col_key, m_target_link); } - else { - REALM_ASSERT(false); - } - } - else if (attr.test(col_attr_Dictionary)) { - auto dict = this->get_dictionary(origin_col_key); - Mixed val{target_link}; - for (auto it : dict) { - if (it.second == val) { - dict.nullify(it.first); + void on_dictionary(Dictionary dict) final + { + Mixed val{m_target_link}; + for (auto it : dict) { + if (it.second == val) { + dict.nullify(it.first); + } } } - } - else { - Array fallback(alloc); - Array& fields = get_tree_top()->get_fields_accessor(fallback, m_mem); - - if (origin_col_key.get_type() == col_type_Link) { - ArrayKey links(alloc); - links.set_parent(&fields, origin_col_ndx.val + 1); - links.init_from_parent(); - - // Ensure we are nullifying correct link - REALM_ASSERT(links.get(m_row_ndx) == target_link.get_obj_key()); - - links.set(m_row_ndx, ObjKey{}); + void on_link_property(ColKey origin_col_key) final + { + m_origin_obj.nullify_single_link(origin_col_key, m_target_link.get_obj_key()); } - else if (origin_col_key.get_type() == col_type_TypedLink) { - ArrayTypedLink links(alloc); - links.set_parent(&fields, origin_col_ndx.val + 1); - links.init_from_parent(); - - // Ensure we are nullifying correct link - REALM_ASSERT(links.get(m_row_ndx) == target_link); - - links.set(m_row_ndx, ObjLink{}); + void on_mixed_property(ColKey origin_col_key) final + { + m_origin_obj.nullify_single_link(origin_col_key, Mixed{m_target_link}); } - else { - ArrayMixed mixed(alloc); - mixed.set_parent(&fields, origin_col_ndx.val + 1); - mixed.init_from_parent(); - - // Ensure we are nullifying correct link - REALM_ASSERT(mixed.get(m_row_ndx).get() == target_link); - - mixed.set(m_row_ndx, Mixed{}); + void on_typedlink_property(ColKey origin_col_key) final + { + m_origin_obj.nullify_single_link(origin_col_key, m_target_link); } - sync(fields); + private: + ObjLink m_target_link; + } nullifier{*this, origin_col_key, target_link}; + nullifier.run(); - if (Replication* repl = get_replication()) - repl->nullify_link(m_table.unchecked_ptr(), origin_col_key, m_key); // Throws - } - alloc.bump_content_version(); + get_alloc().bump_content_version(); } void Obj::set_backlink(ColKey col_key, ObjLink new_link) const @@ -1934,6 +1971,80 @@ bool Obj::remove_backlink(ColKey col_key, ObjLink old_link, CascadeState& state) return false; } +struct EmbeddedObjectLinkMigrator : public LinkTranslator { + EmbeddedObjectLinkMigrator(Obj origin, ColKey origin_col, Obj dest_orig, Obj dest_replace) + : LinkTranslator(origin, origin_col) + , m_dest_orig(dest_orig) + , m_dest_replace(dest_replace) + { + } + void on_list_of_links(LnkLst list) final + { + auto n = list.find_first(m_dest_orig.get_key()); + REALM_ASSERT(n != realm::npos); + list.set(n, m_dest_replace.get_key()); + } + void on_dictionary(Dictionary dict) final + { + auto pos = dict.find_any(m_dest_orig.get_link()); + REALM_ASSERT(pos != realm::npos); + Mixed key = dict.get_key(pos); + dict.insert(key, m_dest_replace.get_link()); + } + void on_link_property(ColKey col) final + { + REALM_ASSERT(!m_origin_obj.get(col) || m_origin_obj.get(col) == m_dest_orig.get_key()); + m_origin_obj.set(col, m_dest_replace.get_key()); + } + void on_set_of_links(LnkSet) final + { + // this should never happen because sets of embedded objects are not allowed at the schema level + REALM_UNREACHABLE(); + } + // The following cases have support here but are expected to fail later on in the + // migration due to core not yet supporting untyped Mixed links to embedded objects. + void on_set_of_mixed(Set set) final + { + auto did_erase_pair = set.erase(m_dest_orig.get_link()); + REALM_ASSERT(did_erase_pair.second); + set.insert(m_dest_replace.get_link()); + } + void on_set_of_typedlink(Set set) final + { + auto did_erase_pair = set.erase(m_dest_orig.get_link()); + REALM_ASSERT(did_erase_pair.second); + set.insert(m_dest_replace.get_link()); + } + void on_list_of_mixed(Lst list) final + { + auto n = list.find_any(m_dest_orig.get_link()); + REALM_ASSERT(n != realm::npos); + list.insert_any(n, m_dest_replace.get_link()); + } + void on_list_of_typedlink(Lst list) final + { + auto n = list.find_any(m_dest_orig.get_link()); + REALM_ASSERT(n != realm::npos); + list.insert_any(n, m_dest_replace.get_link()); + } + void on_mixed_property(ColKey col) final + { + REALM_ASSERT(m_origin_obj.get(col).is_null() || + m_origin_obj.get(col) == m_dest_orig.get_link()); + m_origin_obj.set_any(col, m_dest_replace.get_link()); + } + void on_typedlink_property(ColKey col) final + { + REALM_ASSERT(m_origin_obj.get(col).is_null() || + m_origin_obj.get(col) == m_dest_orig.get_link()); + m_origin_obj.set(col, m_dest_replace.get_link()); + } + +private: + Obj m_dest_orig; + Obj m_dest_replace; +}; + void Obj::handle_multiple_backlinks_during_schema_migration() { REALM_ASSERT(!m_table->get_primary_key_column()); @@ -1947,7 +2058,9 @@ void Obj::handle_multiple_backlinks_during_schema_migration() auto obj = m_table->create_object(); embedded_obj_tracker->track(*this, obj); auto linking_obj = opposite_table->get_object(backlink); - fix_linking_object_during_schema_migration(linking_obj, obj, opposite_column); + // change incoming links to point to the newly created object + EmbeddedObjectLinkMigrator migrator{linking_obj, opposite_column, *this, obj}; + migrator.run(); } embedded_obj_tracker->process_pending(); return false; @@ -1955,47 +2068,6 @@ void Obj::handle_multiple_backlinks_during_schema_migration() m_table->for_each_backlink_column(copy_links); } -void Obj::fix_linking_object_during_schema_migration(Obj linking_obj, Obj obj, ColKey opposite_col_key) const -{ - // fix linking obj links in order to point to the new object just created - if (opposite_col_key.get_type() == col_type_LinkList) { - auto linking_obj_list = linking_obj.get_linklist(opposite_col_key); - auto n = linking_obj_list.find_first(get_key()); - REALM_ASSERT(n != realm::npos); - linking_obj_list.set(n, obj.get_key()); - } - else if (opposite_col_key.get_attrs().test(col_attr_List)) { - auto linking_obj_list = linking_obj.get_listbase_ptr(opposite_col_key); - auto n = linking_obj_list->find_any(ObjLink{m_table->get_key(), get_key()}); - REALM_ASSERT(n != realm::npos); - linking_obj_list->insert_any(n, ObjLink{m_table->get_key(), obj.get_key()}); - } - else if (opposite_col_key.get_attrs().test(col_attr_Set)) { - // this SHOULD NEVER HAPPEN, since SET sematincs forbids to have a backlink to the same object store - // multiple times. update_schema would have thrown way before to reach this point. - REALM_UNREACHABLE(); - } - else if (opposite_col_key.get_attrs().test(col_attr_Dictionary)) { - auto linking_obj_dictionary = linking_obj.get_dictionary_ptr(opposite_col_key); - auto pos = linking_obj_dictionary->find_any(ObjLink{m_table->get_key(), get_key()}); - REALM_ASSERT(pos != realm::npos); - Mixed key = linking_obj_dictionary->get_key(pos); - linking_obj_dictionary->insert(key, ObjLink{m_table->get_key(), obj.get_key()}); - } - else if (opposite_col_key.get_type() == col_type_Mixed && - linking_obj.get_any(opposite_col_key).get_type() == type_TypedLink) { - REALM_ASSERT(!linking_obj.get(opposite_col_key) || - linking_obj.get(opposite_col_key) == get_key()); - linking_obj.set_any(opposite_col_key, ObjLink{m_table->get_key(), obj.get_key()}); - } - else if (opposite_col_key.get_type() == col_type_Link) { - // Single link - REALM_ASSERT(!linking_obj.get(opposite_col_key) || - linking_obj.get(opposite_col_key) == get_key()); - linking_obj.set(opposite_col_key, obj.get_key()); - } -} - Dictionary Obj::get_dictionary(ColKey col_key) const { REALM_ASSERT(col_key.is_dictionary()); @@ -2049,6 +2121,83 @@ LinkCollectionPtr Obj::get_linkcollection_ptr(ColKey col_key) const void Obj::assign_pk_and_backlinks(const Obj& other) { + struct LinkReplacer : LinkTranslator { + LinkReplacer(Obj origin, ColKey origin_col_key, const Obj& dest_orig, const Obj& dest_replace) + : LinkTranslator(origin, origin_col_key) + , m_dest_orig(dest_orig) + , m_dest_replace(dest_replace) + { + } + void on_list_of_links(LnkLst) final + { + // using Lst for direct access without hiding unresolved keys + auto list = m_origin_obj.get_list(m_origin_col_key); + auto n = list.find_first(m_dest_orig.get_key()); + REALM_ASSERT(n != realm::npos); + list.set(n, m_dest_replace.get_key()); + } + void on_list_of_mixed(Lst list) final + { + auto n = list.find_first(m_dest_orig.get_link()); + REALM_ASSERT(n != realm::npos); + list.set(n, m_dest_replace.get_link()); + } + void on_list_of_typedlink(Lst list) final + { + auto n = list.find_first(m_dest_orig.get_link()); + REALM_ASSERT(n != realm::npos); + list.set(n, m_dest_replace.get_link()); + } + void on_set_of_links(LnkSet) final + { + // using Set for direct access without hiding unresolved keys + auto set = m_origin_obj.get_set(m_origin_col_key); + set.erase(m_dest_orig.get_key()); + set.insert(m_dest_replace.get_key()); + } + void on_set_of_mixed(Set set) final + { + set.erase(m_dest_orig.get_link()); + set.insert(m_dest_replace.get_link()); + } + void on_set_of_typedlink(Set set) final + { + set.erase(m_dest_orig.get_link()); + set.insert(m_dest_replace.get_link()); + } + void on_dictionary(Dictionary dict) final + { + Mixed val(m_dest_orig.get_link()); + for (auto it : dict) { + if (it.second == val) { + auto link = m_dest_replace.get_link(); + dict.insert(it.first, link); + } + } + } + void on_link_property(ColKey col) final + { + REALM_ASSERT(!m_origin_obj.get(col) || m_origin_obj.get(col) == m_dest_orig.get_key()); + m_origin_obj.set(col, m_dest_replace.get_key()); + } + void on_mixed_property(ColKey col) final + { + REALM_ASSERT(m_origin_obj.get_any(col).is_null() || + m_origin_obj.get_any(col).get_link().get_obj_key() == m_dest_orig.get_key()); + m_origin_obj.set(col, Mixed{m_dest_replace.get_link()}); + } + void on_typedlink_property(ColKey col) final + { + REALM_ASSERT(m_origin_obj.get_any(col).is_null() || + m_origin_obj.get_any(col).get_link().get_obj_key() == m_dest_orig.get_key()); + m_origin_obj.set(col, m_dest_replace.get_link()); + } + + private: + const Obj& m_dest_orig; + const Obj& m_dest_replace; + }; + REALM_ASSERT(get_table() == other.get_table()); if (auto col_pk = m_table->get_primary_key_column()) { Mixed val = other.get_any(col_pk); @@ -2066,68 +2215,8 @@ void Obj::assign_pk_and_backlinks(const Obj& other) auto backlinks = other.get_all_backlinks(col); for (auto bl : backlinks) { auto linking_obj = t->get_object(bl); - if (c.is_dictionary()) { - auto dict = linking_obj.get_dictionary(c); - Mixed val(other.get_link()); - for (auto it : dict) { - if (it.second == val) { - auto link = get_link(); - dict.insert(it.first, link); - } - } - } - else if (c.is_set()) { - if (c.get_type() == col_type_Link) { - auto set = linking_obj.get_set(c); - set.erase(other.get_key()); - set.insert(get_key()); - } - else if (c.get_type() == col_type_TypedLink) { - auto set = linking_obj.get_set(c); - set.erase({m_table->get_key(), other.get_key()}); - set.insert({m_table->get_key(), get_key()}); - } - if (c.get_type() == col_type_Mixed) { - auto set = linking_obj.get_set(c); - set.erase(ObjLink{m_table->get_key(), other.get_key()}); - set.insert(ObjLink{m_table->get_key(), get_key()}); - } - } - else if (c.is_list()) { - if (c.get_type() == col_type_Mixed) { - auto l = linking_obj.get_list(c); - auto n = l.find_first(ObjLink{m_table->get_key(), other.get_key()}); - REALM_ASSERT(n != realm::npos); - l.set(n, ObjLink{m_table->get_key(), get_key()}); - } - else if (c.get_type() == col_type_LinkList) { - // Link list - auto l = linking_obj.get_list(c); - auto n = l.find_first(other.get_key()); - REALM_ASSERT(n != realm::npos); - l.set(n, get_key()); - } - else { - REALM_UNREACHABLE(); // missing type handling - } - } - else { - REALM_ASSERT(!c.is_collection()); - if (c.get_type() == col_type_Link) { - // Single link - REALM_ASSERT(!linking_obj.get(c) || linking_obj.get(c) == other.get_key()); - linking_obj.set(c, get_key()); - } - else if (c.get_type() == col_type_Mixed) { - // Mixed link - REALM_ASSERT(linking_obj.get_any(c).is_null() || - linking_obj.get_any(c).get_link().get_obj_key() == other.get_key()); - linking_obj.set(c, Mixed{ObjLink{m_table->get_key(), get_key()}}); - } - else { - REALM_UNREACHABLE(); // missing type handling - } - } + LinkReplacer replacer{linking_obj, c, other, *this}; + replacer.run(); } return false; }; diff --git a/src/realm/obj.hpp b/src/realm/obj.hpp index 8839c80a138..ca6171eda53 100644 --- a/src/realm/obj.hpp +++ b/src/realm/obj.hpp @@ -426,6 +426,8 @@ class Obj { bool remove_backlink(ColKey col_key, ObjLink old_link, CascadeState& state) const; template inline void set_spec(T&, ColKey); + template + inline void nullify_single_link(ColKey col, ValueType target); void fix_linking_object_during_schema_migration(Obj linking_obj, Obj obj, ColKey opposite_col_key) const; }; diff --git a/src/realm/table.cpp b/src/realm/table.cpp index 665dcde4954..52050a7c075 100644 --- a/src/realm/table.cpp +++ b/src/realm/table.cpp @@ -333,6 +333,72 @@ void LinkChain::add(ColKey ck) m_link_cols.push_back(ck); } +LinkTranslator::LinkTranslator(Obj origin, ColKey origin_col_key) + : m_origin_obj(origin) + , m_origin_col_key(origin_col_key) +{ +} + +void LinkTranslator::run() +{ + ColumnAttrMask attr = m_origin_col_key.get_attrs(); + if (attr.test(col_attr_List)) { + if (m_origin_col_key.get_type() == col_type_LinkList) { + LnkLst link_list = m_origin_obj.get_linklist(m_origin_col_key); + on_list_of_links(link_list); + } + else if (m_origin_col_key.get_type() == col_type_Mixed) { + Lst list = m_origin_obj.get_list(m_origin_col_key); + on_list_of_mixed(list); + } + else if (m_origin_col_key.get_type() == col_type_TypedLink) { + Lst list = m_origin_obj.get_list(m_origin_col_key); + on_list_of_typedlink(list); + } + else { + throw std::runtime_error( + util::format("LinkTranslator unhandled list type: %1", m_origin_col_key.get_type())); + } + } + else if (attr.test(col_attr_Set)) { + if (m_origin_col_key.get_type() == col_type_Link) { + LnkSet set = m_origin_obj.get_linkset(m_origin_col_key); + on_set_of_links(set); + } + else if (m_origin_col_key.get_type() == col_type_Mixed) { + Set set = m_origin_obj.get_set(m_origin_col_key); + on_set_of_mixed(set); + } + else if (m_origin_col_key.get_type() == col_type_TypedLink) { + Set set = m_origin_obj.get_set(m_origin_col_key); + on_set_of_typedlink(set); + } + else { + throw std::runtime_error( + util::format("LinkTranslator unhandled set type: %1", m_origin_col_key.get_type())); + } + } + else if (attr.test(col_attr_Dictionary)) { + auto dict = m_origin_obj.get_dictionary(m_origin_col_key); + on_dictionary(dict); + } + else { + REALM_ASSERT_EX(!m_origin_col_key.is_collection(), m_origin_col_key); + if (m_origin_col_key.get_type() == col_type_Link) { + on_link_property(m_origin_col_key); + } + else if (m_origin_col_key.get_type() == col_type_Mixed) { + on_mixed_property(m_origin_col_key); + } + else if (m_origin_col_key.get_type() == col_type_TypedLink) { + on_typedlink_property(m_origin_col_key); + } + else { + throw std::runtime_error( + util::format("LinkTranslator unhandled property type: %1", m_origin_col_key.get_type())); + } + } +} // -- Table --------------------------------------------------------------------------------- @@ -1115,15 +1181,34 @@ void Table::set_embedded(bool embedded) } else if (size() > 0) { for (auto object : *this) { - size_t backlink_count = object.get_backlink_count(); + size_t backlink_count = 0; + for_each_backlink_column([&](ColKey backlink_col_key) { + size_t cur_backlinks = object.get_backlink_cnt(backlink_col_key); + if (cur_backlinks > 0) { + // Make sure this link is not an untyped ObjLink which lacks core support in many places + ColKey source_col = get_opposite_column(backlink_col_key); + REALM_ASSERT(source_col); // backlink columns should always have a source + TableRef source_table = get_opposite_table(backlink_col_key); + ColKey forward_col_mapped = source_table->get_opposite_column(source_col); + if (!forward_col_mapped) { + throw std::logic_error(util::format("There is a dynamic/untyped link from a Mixed property " + "'%1.%2' which prevents migrating class '%3' to embedded", + source_table->get_name(), + source_table->get_column_name(source_col), get_name())); + } + } + backlink_count += cur_backlinks; + if (backlink_count > 1) { + throw std::logic_error( + util::format("At least one object in '%1' does have multiple backlinks.", get_name())); + } + return false; // continue + }); + if (backlink_count == 0) { throw std::logic_error(util::format( "At least one object in '%1' does not have a backlink (data would get lost).", get_name())); } - else if (backlink_count > 1) { - throw std::logic_error( - util::format("At least one object in '%1' does have multiple backlinks.", get_name())); - } } } diff --git a/src/realm/table.hpp b/src/realm/table.hpp index dace3ebbbde..e1d4e34c0d1 100644 --- a/src/realm/table.hpp +++ b/src/realm/table.hpp @@ -1095,6 +1095,25 @@ class LinkChain { } }; +struct LinkTranslator { + LinkTranslator(Obj origin, ColKey origin_col_key); + void run(); + virtual void on_list_of_links(LnkLst list) = 0; + virtual void on_list_of_mixed(Lst list) = 0; + virtual void on_list_of_typedlink(Lst list) = 0; + virtual void on_set_of_links(LnkSet set) = 0; + virtual void on_set_of_mixed(Set set) = 0; + virtual void on_set_of_typedlink(Set set) = 0; + virtual void on_dictionary(Dictionary dict) = 0; + virtual void on_link_property(ColKey col) = 0; + virtual void on_mixed_property(ColKey col) = 0; + virtual void on_typedlink_property(ColKey col) = 0; + +protected: + Obj m_origin_obj; + ColKey m_origin_col_key; +}; + // Implementation: inline ColKeys Table::get_column_keys() const diff --git a/test/object-store/migrations.cpp b/test/object-store/migrations.cpp index 57e60ebe19c..044f69dc4ab 100644 --- a/test/object-store/migrations.cpp +++ b/test/object-store/migrations.cpp @@ -19,6 +19,7 @@ #include #include "util/test_file.hpp" +#include "util/test_utils.hpp" #include #include @@ -696,6 +697,105 @@ TEST_CASE("migration: Automatic") { parent_object.set_property_value(context, "child_property", std::any(child_object)); })); } + + SECTION("Migrations to embedded object with untyped Mixed links") { + auto setup_mixed_link = [&](PropertyType type) -> SharedRealm { + InMemoryTestFile config; + config.automatic_handle_backlicks_in_migrations = true; + Schema schema = { + { + "child_table", + {{"value", PropertyType::Int}}, + }, + {"parent_table", + { + {"child_property", type}, + }}, + }; + auto realm = Realm::get_shared_realm(config); + realm->update_schema(schema, 1); + realm->begin_transaction(); + auto child_table = ObjectStore::table_for_object_type(realm->read_group(), "child_table"); + Obj child_object = child_table->create_object(); + child_object.set("value", 42); + auto parent_table = ObjectStore::table_for_object_type(realm->read_group(), "parent_table"); + auto parent_object = parent_table->create_object(); + auto child_object_key = child_object.get_key(); + ColKey child_col_key = parent_table->get_column_key("child_property"); + + REALM_ASSERT(child_col_key.get_type() == col_type_Mixed); + Mixed child_link = ObjLink{child_table->get_key(), child_object_key}; + if (child_col_key.is_set()) { + auto set = parent_object.get_set(child_col_key); + set.insert(child_link); + } + else if (child_col_key.is_list()) { + auto list = parent_object.get_list(child_col_key); + list.insert(0, child_link); + list.insert(1, child_link); + } + else if (child_col_key.is_dictionary()) { + auto dict = parent_object.get_dictionary(child_col_key); + dict.insert("foo", child_link); + dict.insert("bar", child_link); + } + else { + REALM_ASSERT(!child_col_key.is_collection()); + parent_object.set_any(child_col_key, child_link); + } + realm->commit_transaction(); + REQUIRE(parent_table->size() == 1); + REQUIRE(child_table->size() == 1); + REQUIRE_FALSE(child_table->is_embedded()); + REQUIRE_FALSE(parent_table->is_embedded()); + return realm; + }; + auto post_check_failed_migration = [](SharedRealm realm) { + auto parent_table = ObjectStore::table_for_object_type(realm->read_group(), "parent_table"); + auto child_table = ObjectStore::table_for_object_type(realm->read_group(), "child_table"); + REQUIRE(realm->schema_version() == 1); + REQUIRE(parent_table->size() == 1); + REQUIRE(child_table->size() == 1); + REQUIRE(!child_table->is_embedded()); + }; + const std::string expected_message = + "There is a dynamic/untyped link from a Mixed property 'class_parent_table.child_property' which " + "prevents migrating class 'class_child_table' to embedded"; + + SECTION("List") { + auto realm = setup_mixed_link(PropertyType::Mixed | PropertyType::Nullable | PropertyType::Array); + REQUIRE_THROWS_CONTAINING( + realm->update_schema(set_table_type(realm->schema(), "child_table", ObjectType::Embedded), 2, + [](auto, auto, auto&) {}), + expected_message); + post_check_failed_migration(realm); + } + SECTION("Set") { + auto realm = setup_mixed_link(PropertyType::Mixed | PropertyType::Nullable | PropertyType::Set); + REQUIRE_THROWS_CONTAINING( + realm->update_schema(set_table_type(realm->schema(), "child_table", ObjectType::Embedded), 2, + [](auto, auto, auto&) {}), + expected_message); + post_check_failed_migration(realm); + } + SECTION("Dictionary") { + auto realm = + setup_mixed_link(PropertyType::Mixed | PropertyType::Nullable | PropertyType::Dictionary); + REQUIRE_THROWS_CONTAINING( + realm->update_schema(set_table_type(realm->schema(), "child_table", ObjectType::Embedded), 2, + [](auto, auto, auto&) {}), + expected_message); + post_check_failed_migration(realm); + } + SECTION("Mixed property") { + auto realm = setup_mixed_link(PropertyType::Mixed | PropertyType::Nullable); + REQUIRE_THROWS_CONTAINING( + realm->update_schema(set_table_type(realm->schema(), "child_table", ObjectType::Embedded), 2, + [](auto, auto, auto&) {}), + expected_message); + post_check_failed_migration(realm); + } + } } SECTION("valid migrations") { From be7f7532963365e32572be60871b735253e4754a Mon Sep 17 00:00:00 2001 From: James Stone Date: Wed, 31 Aug 2022 10:31:14 -0700 Subject: [PATCH 2/4] LinkTranslator gets a separate file --- Package.swift | 1 + src/realm/CMakeLists.txt | 1 + src/realm/link_translator.cpp | 94 +++++++++++++++++++++++++++++++++++ src/realm/link_translator.hpp | 47 ++++++++++++++++++ src/realm/obj.cpp | 1 + src/realm/table.cpp | 67 ------------------------- src/realm/table.hpp | 19 ------- 7 files changed, 144 insertions(+), 86 deletions(-) create mode 100644 src/realm/link_translator.cpp create mode 100644 src/realm/link_translator.hpp diff --git a/Package.swift b/Package.swift index 4855e76dcaa..10dd3a98e73 100644 --- a/Package.swift +++ b/Package.swift @@ -75,6 +75,7 @@ let notSyncServerSources: [String] = [ "realm/history.cpp", "realm/impl", "realm/index_string.cpp", + "realm/link_translator.cpp", "realm/list.cpp", "realm/mixed.cpp", "realm/node.cpp", diff --git a/src/realm/CMakeLists.txt b/src/realm/CMakeLists.txt index 993ca38fc08..6f211fb0a51 100644 --- a/src/realm/CMakeLists.txt +++ b/src/realm/CMakeLists.txt @@ -41,6 +41,7 @@ set(REALM_SOURCES impl/simulated_failure.cpp impl/transact_log.cpp index_string.cpp + link_translator.cpp list.cpp node.cpp mixed.cpp diff --git a/src/realm/link_translator.cpp b/src/realm/link_translator.cpp new file mode 100644 index 00000000000..253ad68b89c --- /dev/null +++ b/src/realm/link_translator.cpp @@ -0,0 +1,94 @@ +/************************************************************************* + * + * Copyright 2022 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. + * + **************************************************************************/ + +#include "realm/link_translator.hpp" + +#include +#include +#include + +namespace realm { + +LinkTranslator::LinkTranslator(Obj origin, ColKey origin_col_key) + : m_origin_obj(origin) + , m_origin_col_key(origin_col_key) +{ +} + +void LinkTranslator::run() +{ + ColumnAttrMask attr = m_origin_col_key.get_attrs(); + if (attr.test(col_attr_List)) { + if (m_origin_col_key.get_type() == col_type_LinkList) { + LnkLst link_list = m_origin_obj.get_linklist(m_origin_col_key); + on_list_of_links(link_list); + } + else if (m_origin_col_key.get_type() == col_type_Mixed) { + Lst list = m_origin_obj.get_list(m_origin_col_key); + on_list_of_mixed(list); + } + else if (m_origin_col_key.get_type() == col_type_TypedLink) { + Lst list = m_origin_obj.get_list(m_origin_col_key); + on_list_of_typedlink(list); + } + else { + throw std::runtime_error( + util::format("LinkTranslator unhandled list type: %1", m_origin_col_key.get_type())); + } + } + else if (attr.test(col_attr_Set)) { + if (m_origin_col_key.get_type() == col_type_Link) { + LnkSet set = m_origin_obj.get_linkset(m_origin_col_key); + on_set_of_links(set); + } + else if (m_origin_col_key.get_type() == col_type_Mixed) { + Set set = m_origin_obj.get_set(m_origin_col_key); + on_set_of_mixed(set); + } + else if (m_origin_col_key.get_type() == col_type_TypedLink) { + Set set = m_origin_obj.get_set(m_origin_col_key); + on_set_of_typedlink(set); + } + else { + throw std::runtime_error( + util::format("LinkTranslator unhandled set type: %1", m_origin_col_key.get_type())); + } + } + else if (attr.test(col_attr_Dictionary)) { + auto dict = m_origin_obj.get_dictionary(m_origin_col_key); + on_dictionary(dict); + } + else { + REALM_ASSERT_EX(!m_origin_col_key.is_collection(), m_origin_col_key); + if (m_origin_col_key.get_type() == col_type_Link) { + on_link_property(m_origin_col_key); + } + else if (m_origin_col_key.get_type() == col_type_Mixed) { + on_mixed_property(m_origin_col_key); + } + else if (m_origin_col_key.get_type() == col_type_TypedLink) { + on_typedlink_property(m_origin_col_key); + } + else { + throw std::runtime_error( + util::format("LinkTranslator unhandled property type: %1", m_origin_col_key.get_type())); + } + } +} + +} // namespace realm diff --git a/src/realm/link_translator.hpp b/src/realm/link_translator.hpp new file mode 100644 index 00000000000..051d07ad6ce --- /dev/null +++ b/src/realm/link_translator.hpp @@ -0,0 +1,47 @@ +/************************************************************************* + * + * Copyright 2022 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_LINK_TRANSLATOR_HPP +#define REALM_LINK_TRANSLATOR_HPP + +#include + +namespace realm { + +struct LinkTranslator { + LinkTranslator(Obj origin, ColKey origin_col_key); + void run(); + virtual void on_list_of_links(LnkLst list) = 0; + virtual void on_list_of_mixed(Lst list) = 0; + virtual void on_list_of_typedlink(Lst list) = 0; + virtual void on_set_of_links(LnkSet set) = 0; + virtual void on_set_of_mixed(Set set) = 0; + virtual void on_set_of_typedlink(Set set) = 0; + virtual void on_dictionary(Dictionary dict) = 0; + virtual void on_link_property(ColKey col) = 0; + virtual void on_mixed_property(ColKey col) = 0; + virtual void on_typedlink_property(ColKey col) = 0; + +protected: + Obj m_origin_obj; + ColKey m_origin_col_key; +}; + +} // namespace realm + +#endif // REALM_LINK_TRANSLATOR_HPP diff --git a/src/realm/obj.cpp b/src/realm/obj.cpp index e2c66f3d60f..e4c3a3a58fa 100644 --- a/src/realm/obj.cpp +++ b/src/realm/obj.cpp @@ -32,6 +32,7 @@ #include "realm/cluster_tree.hpp" #include "realm/column_type_traits.hpp" #include "realm/dictionary.hpp" +#include "realm/link_translator.hpp" #include "realm/index_string.hpp" #include "realm/object_converter.hpp" #include "realm/replication.hpp" diff --git a/src/realm/table.cpp b/src/realm/table.cpp index 52050a7c075..288949c97a5 100644 --- a/src/realm/table.cpp +++ b/src/realm/table.cpp @@ -333,73 +333,6 @@ void LinkChain::add(ColKey ck) m_link_cols.push_back(ck); } -LinkTranslator::LinkTranslator(Obj origin, ColKey origin_col_key) - : m_origin_obj(origin) - , m_origin_col_key(origin_col_key) -{ -} - -void LinkTranslator::run() -{ - ColumnAttrMask attr = m_origin_col_key.get_attrs(); - if (attr.test(col_attr_List)) { - if (m_origin_col_key.get_type() == col_type_LinkList) { - LnkLst link_list = m_origin_obj.get_linklist(m_origin_col_key); - on_list_of_links(link_list); - } - else if (m_origin_col_key.get_type() == col_type_Mixed) { - Lst list = m_origin_obj.get_list(m_origin_col_key); - on_list_of_mixed(list); - } - else if (m_origin_col_key.get_type() == col_type_TypedLink) { - Lst list = m_origin_obj.get_list(m_origin_col_key); - on_list_of_typedlink(list); - } - else { - throw std::runtime_error( - util::format("LinkTranslator unhandled list type: %1", m_origin_col_key.get_type())); - } - } - else if (attr.test(col_attr_Set)) { - if (m_origin_col_key.get_type() == col_type_Link) { - LnkSet set = m_origin_obj.get_linkset(m_origin_col_key); - on_set_of_links(set); - } - else if (m_origin_col_key.get_type() == col_type_Mixed) { - Set set = m_origin_obj.get_set(m_origin_col_key); - on_set_of_mixed(set); - } - else if (m_origin_col_key.get_type() == col_type_TypedLink) { - Set set = m_origin_obj.get_set(m_origin_col_key); - on_set_of_typedlink(set); - } - else { - throw std::runtime_error( - util::format("LinkTranslator unhandled set type: %1", m_origin_col_key.get_type())); - } - } - else if (attr.test(col_attr_Dictionary)) { - auto dict = m_origin_obj.get_dictionary(m_origin_col_key); - on_dictionary(dict); - } - else { - REALM_ASSERT_EX(!m_origin_col_key.is_collection(), m_origin_col_key); - if (m_origin_col_key.get_type() == col_type_Link) { - on_link_property(m_origin_col_key); - } - else if (m_origin_col_key.get_type() == col_type_Mixed) { - on_mixed_property(m_origin_col_key); - } - else if (m_origin_col_key.get_type() == col_type_TypedLink) { - on_typedlink_property(m_origin_col_key); - } - else { - throw std::runtime_error( - util::format("LinkTranslator unhandled property type: %1", m_origin_col_key.get_type())); - } - } -} - // -- Table --------------------------------------------------------------------------------- ColKey Table::add_column(DataType type, StringData name, bool nullable) diff --git a/src/realm/table.hpp b/src/realm/table.hpp index e1d4e34c0d1..dace3ebbbde 100644 --- a/src/realm/table.hpp +++ b/src/realm/table.hpp @@ -1095,25 +1095,6 @@ class LinkChain { } }; -struct LinkTranslator { - LinkTranslator(Obj origin, ColKey origin_col_key); - void run(); - virtual void on_list_of_links(LnkLst list) = 0; - virtual void on_list_of_mixed(Lst list) = 0; - virtual void on_list_of_typedlink(Lst list) = 0; - virtual void on_set_of_links(LnkSet set) = 0; - virtual void on_set_of_mixed(Set set) = 0; - virtual void on_set_of_typedlink(Set set) = 0; - virtual void on_dictionary(Dictionary dict) = 0; - virtual void on_link_property(ColKey col) = 0; - virtual void on_mixed_property(ColKey col) = 0; - virtual void on_typedlink_property(ColKey col) = 0; - -protected: - Obj m_origin_obj; - ColKey m_origin_col_key; -}; - // Implementation: inline ColKeys Table::get_column_keys() const From eeb942db9af1e9ece788edd70117b7a20a9a8fca Mon Sep 17 00:00:00 2001 From: James Stone Date: Wed, 31 Aug 2022 10:34:13 -0700 Subject: [PATCH 3/4] update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 81845eef183..f8367eb7847 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,12 +15,12 @@ ### Fixed * ([#????](https://github.com/realm/realm-core/issues/????), since v?.?.?) +* Prevent migrations to an embedded object type when there are incoming links from Mixed/TypedLink properties until we can support them. ([#5796](https://github.com/realm/realm-core/pull/5796)) * Fixed undefined behaviour on queries involving a constant and an indexed column on some property types like UUID and Timestamp. ([#5753](https://github.com/realm/realm-core/issues/5753), since 12.5.0) * Fix all UBSan failures hit by tests. It is unclear if any of these manifested as visible bugs. ([PR #5665](https://github.com/realm/realm-core/pull/5665)) * Fix sorting order for `realm_query_find_first` in the C API.([#5720](https://github.com/realm/realm-core/issues/5720)) * Upload completion callbacks may have called before the download message that completed them was fully integrated. ([#4865](https://github.com/realm/realm-core/issues/4865)). - ### Breaking changes * None. From b8cac8f43bffc9b931a7feee79579df4f475a931 Mon Sep 17 00:00:00 2001 From: James Stone Date: Mon, 12 Sep 2022 11:59:12 -0700 Subject: [PATCH 4/4] review feedback --- src/realm/link_translator.hpp | 23 ++++++++----- src/realm/list.cpp | 9 ++--- src/realm/obj.cpp | 65 ++++++++++++++++++----------------- src/realm/obj.hpp | 2 +- 4 files changed, 52 insertions(+), 47 deletions(-) diff --git a/src/realm/link_translator.hpp b/src/realm/link_translator.hpp index 051d07ad6ce..27039eb8abf 100644 --- a/src/realm/link_translator.hpp +++ b/src/realm/link_translator.hpp @@ -23,16 +23,23 @@ namespace realm { -struct LinkTranslator { +// This construct is used when code needs to handle all +// possible link column types. Subclass and override all +// methods to handle each type. A good example of where +// this is useful is when following a backlink to its +// origin column and modifying the outgoing link from +// whatever container it came from. +class LinkTranslator { +public: LinkTranslator(Obj origin, ColKey origin_col_key); void run(); - virtual void on_list_of_links(LnkLst list) = 0; - virtual void on_list_of_mixed(Lst list) = 0; - virtual void on_list_of_typedlink(Lst list) = 0; - virtual void on_set_of_links(LnkSet set) = 0; - virtual void on_set_of_mixed(Set set) = 0; - virtual void on_set_of_typedlink(Set set) = 0; - virtual void on_dictionary(Dictionary dict) = 0; + virtual void on_list_of_links(LnkLst& list) = 0; + virtual void on_list_of_mixed(Lst& list) = 0; + virtual void on_list_of_typedlink(Lst& list) = 0; + virtual void on_set_of_links(LnkSet& set) = 0; + virtual void on_set_of_mixed(Set& set) = 0; + virtual void on_set_of_typedlink(Set& set) = 0; + virtual void on_dictionary(Dictionary& dict) = 0; virtual void on_link_property(ColKey col) = 0; virtual void on_mixed_property(ColKey col) = 0; virtual void on_typedlink_property(ColKey col) = 0; diff --git a/src/realm/list.cpp b/src/realm/list.cpp index 087692033e4..359d59fe6c1 100644 --- a/src/realm/list.cpp +++ b/src/realm/list.cpp @@ -278,12 +278,9 @@ void Lst::do_clear() ObjKey target_key = m_tree->get(ndx); Obj target_obj = target_table->get_object(target_key); target_obj.remove_one_backlink(backlink_col, m_obj.get_key()); // Throws - size_t num_remaining = target_obj.get_backlink_count(*origin_table, m_col_key); - if (num_remaining == 0) { - // embedded objects should only have one incoming link - REALM_ASSERT_EX(target_obj.get_backlink_count() == 0, target_obj.get_backlink_count()); - state.m_to_be_deleted.emplace_back(target_table_key, target_key); - } + // embedded objects should only have one incoming link + REALM_ASSERT_EX(target_obj.get_backlink_count() == 0, target_obj.get_backlink_count()); + state.m_to_be_deleted.emplace_back(target_table_key, target_key); } m_tree->clear(); diff --git a/src/realm/obj.cpp b/src/realm/obj.cpp index e4c3a3a58fa..fac418b970f 100644 --- a/src/realm/obj.cpp +++ b/src/realm/obj.cpp @@ -723,13 +723,13 @@ void Obj::traverse_path(Visitor v, PathSizer ps, size_t path_length) const , m_dest_obj(dest) { } - void on_list_of_links(LnkLst ll) final + void on_list_of_links(LnkLst& ll) final { auto i = ll.find_first(m_dest_obj.get_key()); REALM_ASSERT(i != realm::npos); m_index = Mixed(int64_t(i)); } - void on_dictionary(Dictionary dict) final + void on_dictionary(Dictionary& dict) final { for (auto it : dict) { if (it.second.is_type(type_TypedLink) && it.second.get_link() == m_dest_obj.get_link()) { @@ -739,23 +739,23 @@ void Obj::traverse_path(Visitor v, PathSizer ps, size_t path_length) const } REALM_ASSERT(!m_index.is_null()); } - void on_list_of_mixed(Lst) final + void on_list_of_mixed(Lst&) final { REALM_UNREACHABLE(); // we don't support Mixed link to embedded object yet } - void on_list_of_typedlink(Lst) final + void on_list_of_typedlink(Lst&) final { REALM_UNREACHABLE(); // we don't support TypedLink to embedded object yet } - void on_set_of_links(LnkSet) final + void on_set_of_links(LnkSet&) final { REALM_UNREACHABLE(); // sets of embedded objects are not allowed at the schema level } - void on_set_of_mixed(Set) final + void on_set_of_mixed(Set&) final { REALM_UNREACHABLE(); // we don't support Mixed link to embedded object yet } - void on_set_of_typedlink(Set) final + void on_set_of_typedlink(Set&) final { REALM_UNREACHABLE(); // we don't support TypedLink to embedded object yet } @@ -1840,13 +1840,14 @@ inline void nullify_set(Obj& obj, ColKey origin_col_key, T target) } } // namespace -template +template inline void Obj::nullify_single_link(ColKey col, ValueType target) { ColKey::Idx origin_col_ndx = col.get_index(); Allocator& alloc = get_alloc(); Array fallback(alloc); Array& fields = get_tree_top()->get_fields_accessor(fallback, m_mem); + using ArrayType = typename ColumnTypeTraits::cluster_leaf_type; ArrayType links(alloc); links.set_parent(&fields, origin_col_ndx.val + 1); links.init_from_parent(); @@ -1870,31 +1871,31 @@ void Obj::nullify_link(ColKey origin_col_key, ObjLink target_link) && , m_target_link(target) { } - void on_list_of_links(LnkLst) final + void on_list_of_links(LnkLst&) final { nullify_linklist(m_origin_obj, m_origin_col_key, m_target_link.get_obj_key()); } - void on_list_of_mixed(Lst) final + void on_list_of_mixed(Lst&) final { nullify_linklist(m_origin_obj, m_origin_col_key, Mixed(m_target_link)); } - void on_list_of_typedlink(Lst) final + void on_list_of_typedlink(Lst&) final { nullify_linklist(m_origin_obj, m_origin_col_key, m_target_link); } - void on_set_of_links(LnkSet) final + void on_set_of_links(LnkSet&) final { nullify_set(m_origin_obj, m_origin_col_key, m_target_link.get_obj_key()); } - void on_set_of_mixed(Set) final + void on_set_of_mixed(Set&) final { nullify_set(m_origin_obj, m_origin_col_key, Mixed(m_target_link)); } - void on_set_of_typedlink(Set) final + void on_set_of_typedlink(Set&) final { nullify_set(m_origin_obj, m_origin_col_key, m_target_link); } - void on_dictionary(Dictionary dict) final + void on_dictionary(Dictionary& dict) final { Mixed val{m_target_link}; for (auto it : dict) { @@ -1905,15 +1906,15 @@ void Obj::nullify_link(ColKey origin_col_key, ObjLink target_link) && } void on_link_property(ColKey origin_col_key) final { - m_origin_obj.nullify_single_link(origin_col_key, m_target_link.get_obj_key()); + m_origin_obj.nullify_single_link(origin_col_key, m_target_link.get_obj_key()); } void on_mixed_property(ColKey origin_col_key) final { - m_origin_obj.nullify_single_link(origin_col_key, Mixed{m_target_link}); + m_origin_obj.nullify_single_link(origin_col_key, Mixed{m_target_link}); } void on_typedlink_property(ColKey origin_col_key) final { - m_origin_obj.nullify_single_link(origin_col_key, m_target_link); + m_origin_obj.nullify_single_link(origin_col_key, m_target_link); } private: @@ -1999,13 +2000,13 @@ struct EmbeddedObjectLinkMigrator : public LinkTranslator { , m_dest_replace(dest_replace) { } - void on_list_of_links(LnkLst list) final + void on_list_of_links(LnkLst& list) final { auto n = list.find_first(m_dest_orig.get_key()); REALM_ASSERT(n != realm::npos); list.set(n, m_dest_replace.get_key()); } - void on_dictionary(Dictionary dict) final + void on_dictionary(Dictionary& dict) final { auto pos = dict.find_any(m_dest_orig.get_link()); REALM_ASSERT(pos != realm::npos); @@ -2017,32 +2018,32 @@ struct EmbeddedObjectLinkMigrator : public LinkTranslator { REALM_ASSERT(!m_origin_obj.get(col) || m_origin_obj.get(col) == m_dest_orig.get_key()); m_origin_obj.set(col, m_dest_replace.get_key()); } - void on_set_of_links(LnkSet) final + void on_set_of_links(LnkSet&) final { // this should never happen because sets of embedded objects are not allowed at the schema level REALM_UNREACHABLE(); } // The following cases have support here but are expected to fail later on in the // migration due to core not yet supporting untyped Mixed links to embedded objects. - void on_set_of_mixed(Set set) final + void on_set_of_mixed(Set& set) final { auto did_erase_pair = set.erase(m_dest_orig.get_link()); REALM_ASSERT(did_erase_pair.second); set.insert(m_dest_replace.get_link()); } - void on_set_of_typedlink(Set set) final + void on_set_of_typedlink(Set& set) final { auto did_erase_pair = set.erase(m_dest_orig.get_link()); REALM_ASSERT(did_erase_pair.second); set.insert(m_dest_replace.get_link()); } - void on_list_of_mixed(Lst list) final + void on_list_of_mixed(Lst& list) final { auto n = list.find_any(m_dest_orig.get_link()); REALM_ASSERT(n != realm::npos); list.insert_any(n, m_dest_replace.get_link()); } - void on_list_of_typedlink(Lst list) final + void on_list_of_typedlink(Lst& list) final { auto n = list.find_any(m_dest_orig.get_link()); REALM_ASSERT(n != realm::npos); @@ -2149,7 +2150,7 @@ void Obj::assign_pk_and_backlinks(const Obj& other) , m_dest_replace(dest_replace) { } - void on_list_of_links(LnkLst) final + void on_list_of_links(LnkLst&) final { // using Lst for direct access without hiding unresolved keys auto list = m_origin_obj.get_list(m_origin_col_key); @@ -2157,36 +2158,36 @@ void Obj::assign_pk_and_backlinks(const Obj& other) REALM_ASSERT(n != realm::npos); list.set(n, m_dest_replace.get_key()); } - void on_list_of_mixed(Lst list) final + void on_list_of_mixed(Lst& list) final { auto n = list.find_first(m_dest_orig.get_link()); REALM_ASSERT(n != realm::npos); list.set(n, m_dest_replace.get_link()); } - void on_list_of_typedlink(Lst list) final + void on_list_of_typedlink(Lst& list) final { auto n = list.find_first(m_dest_orig.get_link()); REALM_ASSERT(n != realm::npos); list.set(n, m_dest_replace.get_link()); } - void on_set_of_links(LnkSet) final + void on_set_of_links(LnkSet&) final { // using Set for direct access without hiding unresolved keys auto set = m_origin_obj.get_set(m_origin_col_key); set.erase(m_dest_orig.get_key()); set.insert(m_dest_replace.get_key()); } - void on_set_of_mixed(Set set) final + void on_set_of_mixed(Set& set) final { set.erase(m_dest_orig.get_link()); set.insert(m_dest_replace.get_link()); } - void on_set_of_typedlink(Set set) final + void on_set_of_typedlink(Set& set) final { set.erase(m_dest_orig.get_link()); set.insert(m_dest_replace.get_link()); } - void on_dictionary(Dictionary dict) final + void on_dictionary(Dictionary& dict) final { Mixed val(m_dest_orig.get_link()); for (auto it : dict) { diff --git a/src/realm/obj.hpp b/src/realm/obj.hpp index 964e08a546a..b36359ac942 100644 --- a/src/realm/obj.hpp +++ b/src/realm/obj.hpp @@ -427,7 +427,7 @@ class Obj { bool remove_backlink(ColKey col_key, ObjLink old_link, CascadeState& state) const; template inline void set_spec(T&, ColKey); - template + template inline void nullify_single_link(ColKey col, ValueType target); void fix_linking_object_during_schema_migration(Obj linking_obj, Obj obj, ColKey opposite_col_key) const;