Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bugfix for client reset with recovery #5957

Merged
merged 1 commit into from
Oct 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
* <How do the end-user experience this issue? what was the impact?> ([#????](https://github.com/realm/realm-core/issues/????), since v?.?.?)
* CompensatingWriteErrorInfo reported string primary keys as boolean values instead ([PR #5938](https://github.com/realm/realm-core/pull/5938), since the introduction of CompensatingWriteErrorInfo in 12.1.0).
* Fix a use-after-free if the last external reference to an encrypted Realm was closed between when a client reset error was received and when the download of the new Realm began. ([PR #5949](https://github.com/realm/realm-core/pull/5949), since 12.4.0).
* Fixed an assertion failure during client reset with recovery when recovering a list operation on an embedded object that has a link column in the path prefix to the list from the top level object. ([PR #5957](https://github.com/realm/realm-core/issues/5957), since introduction of automatic recovery in v11.16.0).

### Breaking changes
* Rename RealmConfig::automatic_handle_backlicks_in_migrations to RealmConfig::automatically_handle_backlinks_in_migrations ([PR #5897](https://github.com/realm/realm-core/pull/5897)).
Expand Down
12 changes: 10 additions & 2 deletions src/realm/sync/noinst/client_reset_recovery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -521,8 +521,7 @@ bool RecoverLocalChangesetsHandler::resolve_path(ListPath& path, Obj remote_obj,
REALM_UNREACHABLE();
}
}
else {
REALM_ASSERT(col.is_dictionary());
else if (col.is_dictionary()) {
++it;
REALM_ASSERT(it != path.end());
REALM_ASSERT(it->type == ListPath::Element::Type::InternKey);
Expand All @@ -538,6 +537,15 @@ bool RecoverLocalChangesetsHandler::resolve_path(ListPath& path, Obj remote_obj,
return false;
}
}
else { // single link to embedded object
// Neither embedded object sets nor Mixed(TypedLink) to embedded objects are supported.
REALM_ASSERT_EX(!col.is_collection(), col);
REALM_ASSERT_EX(col.get_type() == col_type_Link, col);
StringData col_name = remote_obj.get_table()->get_column_name(col);
remote_obj = remote_obj.get_linked_object(col);
local_obj = local_obj.get_linked_object(col_name);
++it;
}
}
return false;
}
Expand Down
17 changes: 16 additions & 1 deletion test/object-store/sync/client_reset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3165,7 +3165,6 @@ TEST_CASE("client reset with embedded object", "[client reset][local][embedded o
}
SECTION("with shared initial state") {
TopLevelContent initial;
initial.link_value = util::none;
test_reset->setup([&](SharedRealm realm) {
auto table = get_table(*realm, "TopLevel");
REQUIRE(table);
Expand Down Expand Up @@ -3201,6 +3200,22 @@ TEST_CASE("client reset with embedded object", "[client reset][local][embedded o
TopLevelContent expected_recovered = local;
reset_embedded_object({local}, {remote}, expected_recovered);
}
SECTION("local ArraySet to an embedded object through a deep link->linklist element which is removed by the "
"remote "
"triggers a list copy") {
local.link_value->array_vals[0] = 12345;
remote.link_value->array_vals.erase(remote.link_value->array_vals.begin());
TopLevelContent expected_recovered = local;
reset_embedded_object({local}, {remote}, expected_recovered);
}
SECTION("local ArrayErase to an embedded object through a deep link->linklist element which is removed by "
"the remote "
"triggers a list copy") {
local.link_value->array_vals.erase(local.link_value->array_vals.begin());
remote.link_value->array_vals.clear();
TopLevelContent expected_recovered = local;
reset_embedded_object({local}, {remote}, expected_recovered);
}
SECTION("local modifications to an embedded object through a linklist cleared by the remote triggers a list "
"copy") {
local.array_values.begin()->name = "modified";
Expand Down