From 0ea2d64f43e8b1492b4ff71ed555cd29b83767ad Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Tue, 27 Feb 2024 08:10:11 -0800 Subject: [PATCH] Don't update backlinks in Mixed self-assignment (#7384) Setting a Mixed field to ObjLink equal to the current value removed the existing backlink and then exited before adding the new one, leaving things in an invalid state. --- CHANGELOG.md | 1 + src/realm/obj.cpp | 11 +++++------ test/test_mixed_null_assertions.cpp | 23 +++++++++++++++++++++++ 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 14ed20fe0d0..4f2b676639e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ ### Fixed * ([#????](https://github.com/realm/realm-core/issues/????), since v?.?.?) * List KVO information was being populated for non-list collections ([PR #7378](https://github.com/realm/realm-core/pull/7378), since v14.0.0) +* Setting a Mixed property to an ObjLink equal to its existing value would remove the existing backlinks and then exit before re-adding them, resulting in later assertion failures due to the backlink state being invalid ([PR #7384](https://github.com/realm/realm-core/pull/7384), since v14.0.0). * Opening file with file format 23 in read-only mode will crash ([#7388](https://github.com/realm/realm-core/issues/7388), since v14.0.0) ### Breaking changes diff --git a/src/realm/obj.cpp b/src/realm/obj.cpp index aaa08a411cf..4d482988076 100644 --- a/src/realm/obj.cpp +++ b/src/realm/obj.cpp @@ -1168,10 +1168,11 @@ Obj& Obj::set(ColKey col_key, Mixed value, bool is_default) } Mixed old_value = get_unfiltered_mixed(col_ndx); - ObjLink old_link{}; - ObjLink new_link{}; if (old_value.is_type(type_TypedLink)) { - old_link = old_value.get(); + if (old_value == value) { + return *this; + } + auto old_link = old_value.get(); recurse = remove_backlink(col_key, old_link, state); } else if (old_value.is_type(type_Dictionary)) { @@ -1187,10 +1188,8 @@ Obj& Obj::set(ColKey col_key, Mixed value, bool is_default) if (m_table->is_asymmetric()) { throw IllegalOperation("Links not allowed in asymmetric tables"); } - new_link = value.template get(); + auto new_link = value.get(); m_table->get_parent_group()->validate(new_link); - if (new_link == old_link) - return *this; set_backlink(col_key, new_link); } diff --git a/test/test_mixed_null_assertions.cpp b/test/test_mixed_null_assertions.cpp index 33a5ea2984a..88aff8c36b6 100644 --- a/test/test_mixed_null_assertions.cpp +++ b/test/test_mixed_null_assertions.cpp @@ -443,3 +443,26 @@ TEST(Mixed_set_non_link_assertion) dest_obj.remove(); // triggers an assertion failure if the backlink was not removed source_obj.remove(); } + +TEST(Mixed_LinkSelfAssignment) +{ + Group g; + auto source = g.add_table("source"); + auto dest = g.add_table("dest"); + ColKey mixed_col = source->add_column(type_Mixed, "mixed"); + auto source_obj = source->create_object(); + auto dest_obj = dest->create_object(); + + CHECK_EQUAL(dest_obj.get_backlink_count(), 0); + + source_obj.set(mixed_col, Mixed{ObjLink{dest->get_key(), dest_obj.get_key()}}); + CHECK_EQUAL(dest_obj.get_backlink_count(), 1); + + // Re-assign the same link, which should not update backlinks + source_obj.set(mixed_col, Mixed{ObjLink{dest->get_key(), dest_obj.get_key()}}); + CHECK_EQUAL(dest_obj.get_backlink_count(), 1); + + dest_obj.remove(); + CHECK_EQUAL(source_obj.get(mixed_col), Mixed()); + source_obj.remove(); +}