diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a349496a00..093a76b8480 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ ### Fixed * ([#????](https://github.com/realm/realm-core/issues/????), since v?.?.?) -* None. +* You may get assertion "n != realm::npos" when integrating changesets from the server. ([#4180](https://github.com/realm/realm-core/pull/4180), since v10.0.0) ### Breaking changes * None. diff --git a/src/realm/cluster.cpp b/src/realm/cluster.cpp index 3fbc530d850..f2e08703c89 100644 --- a/src/realm/cluster.cpp +++ b/src/realm/cluster.cpp @@ -2168,6 +2168,9 @@ Obj ClusterTree::insert(ObjKey k, const FieldValues& values) bool ClusterTree::is_valid(ObjKey k) const { + if (m_size == 0) + return false; + ClusterNode::State state; return m_root->try_get(k, state); } diff --git a/src/realm/obj.cpp b/src/realm/obj.cpp index f10d7d7df47..1fdb879be4d 100644 --- a/src/realm/obj.cpp +++ b/src/realm/obj.cpp @@ -1444,8 +1444,13 @@ void Obj::assign_pk_and_backlinks(const ConstObj& other) Mixed val = other.get_any(col_pk); this->set(col_pk, val); } + auto nb_tombstones = m_table->m_tombstones->size(); - auto copy_links = [this, &other](ColKey col) { + auto copy_links = [this, &other, nb_tombstones](ColKey col) { + if (nb_tombstones != m_table->m_tombstones->size()) { + // Object has been deleted - we are done + return true; + } auto t = m_table->get_opposite_table(col); auto c = m_table->get_opposite_column(col); auto backlinks = other.get_all_backlinks(col); diff --git a/test/test_unresolved_links.cpp b/test/test_unresolved_links.cpp index dc614a063df..3b58b55316b 100644 --- a/test/test_unresolved_links.cpp +++ b/test/test_unresolved_links.cpp @@ -453,4 +453,38 @@ TEST(Unresolved_CondensedIndices) CHECK_EQUAL(list2.size(), 1); } +TEST(Unresolved_Recursive) +{ + Group g; + auto table = g.add_table_with_primary_key("RecursiveNode", type_ObjectId, "_id"); + // Create two link columns. This will create two backlink columns in the target table + // When a tombstone is resurrected, and a backlink in the first backlink column is + // removed and the tombstone is thereby deleted, we should not attempt to find backlinks + // in the second backlink column. + auto col_next = table->add_column_link(type_Link, "NextNode", *table); + auto col_children = table->add_column_link(type_LinkList, "children", *table); + + auto key = table->get_objkey_from_primary_key(ObjectId("5fc929bac4a3964b6d603f4e")); + key = table->create_object_with_primary_key(ObjectId("5fc929bac4a3964b6d603f4d")).set(col_next, key).get_key(); + + table->create_object_with_primary_key(ObjectId("5fc929bac4a3964b6d603f4c")).set(col_next, key); + + // This will delete the tombstone for "5fc929bac4a3964b6d603f4e" + table->create_object_with_primary_key(ObjectId("5fc929bac4a3964b6d603f4e")); + + // The following will ensure that objects will be turned into tombstones when invalidated + auto obj = table->create_object_with_primary_key(ObjectId("5fc929bac4a3964b6d603f4b")); + auto ll = obj.get_linklist(col_children); + ll.add(table->get_objkey_from_primary_key(ObjectId("5fc929bac4a3964b6d603f4c"))); + ll.add(table->get_objkey_from_primary_key(ObjectId("5fc929bac4a3964b6d603f4d"))); + ll.add(table->get_objkey_from_primary_key(ObjectId("5fc929bac4a3964b6d603f4e"))); + + g.verify(); + CHECK_EQUAL(table->nb_unresolved(), 0); + table->get_object_with_primary_key(ObjectId("5fc929bac4a3964b6d603f4c")).invalidate(); + table->get_object_with_primary_key(ObjectId("5fc929bac4a3964b6d603f4d")).invalidate(); + CHECK_EQUAL(table->nb_unresolved(), 2); + g.verify(); +} + #endif