Skip to content

Commit 979a4d2

Browse files
Fix crash when removing dictionary key (#7569)
1 parent 84db3b8 commit 979a4d2

File tree

3 files changed

+49
-3
lines changed

3 files changed

+49
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
* App::all_users() included logged out users only if they were logged out while the App instance existed. It now always includes all logged out users. ([PR #7300](https://github.com/realm/realm-core/pull/7300)).
1313
* Deleting the active user left the active user unset rather than selecting another logged-in user as the active user like logging out and removing users did. ([PR #7300](https://github.com/realm/realm-core/pull/7300)).
1414
* Fix compilation errors when using command-line `swift build` ([#7587](https://github.com/realm/realm-core/pull/7587), since v14.5.1).
15+
* Fixed crash when integrating removal of already removed dictionary key ([#7488](https://github.com/realm/realm-core/issues/7488), since v10.0.0).
1516

1617
### Breaking changes
1718
* The following things have been renamed or moved as part of moving all of the App Services functionality to the app namespace:

src/realm/sync/instruction_applier.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -489,7 +489,7 @@ void InstructionApplier::operator()(const Instruction::Update& instr)
489489
}
490490
},
491491
[&](const Instruction::Payload::Erased&) {
492-
dict.erase(key);
492+
dict.try_erase(key);
493493
},
494494
[&](const Instruction::Payload::ObjectValue&) {
495495
dict.create_and_insert_linked_object(key);

test/test_transform.cpp

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1487,7 +1487,6 @@ TEST(Transform_ArrayInsert_EraseObject)
14871487
client_2.get()->integrate_next_changeset_from(*server);
14881488
}
14891489

1490-
14911490
TEST(Transform_ArrayClearVsArrayClear_TimestampBased)
14921491
{
14931492
auto changeset_dump_dir_gen = get_changeset_dump_dir_generator(test_context);
@@ -2081,7 +2080,9 @@ TEST(Transform_Dictionary)
20812080
auto& tr = *c.group;
20822081
auto table = tr.add_table_with_primary_key("class_Table", type_Int, "id");
20832082
table->add_column_dictionary(type_Mixed, "dict");
2084-
table->create_object_with_primary_key(0);
2083+
auto obj0 = table->create_object_with_primary_key(0);
2084+
auto dict = obj0.get_dictionary("dict");
2085+
dict.insert("key", 42);
20852086
table->create_object_with_primary_key(1);
20862087
});
20872088

@@ -2096,6 +2097,7 @@ TEST(Transform_Dictionary)
20962097
auto dict0 = obj0.get_dictionary("dict");
20972098
auto dict1 = obj1.get_dictionary("dict");
20982099

2100+
dict0.erase("key");
20992101
dict0.insert("a", 123);
21002102
dict0.insert("b", "Hello");
21012103
dict0.insert("c", 45.0);
@@ -2112,6 +2114,7 @@ TEST(Transform_Dictionary)
21122114
auto dict0 = obj0.get_dictionary("dict");
21132115
auto dict1 = obj1.get_dictionary("dict");
21142116

2117+
dict0.erase("key");
21152118
dict0.insert("b", "Hello, World!");
21162119
dict0.insert("d", true);
21172120

@@ -2200,6 +2203,48 @@ TEST(Transform_Set)
22002203
});
22012204
}
22022205

2206+
TEST(Transform_SetEraseVsSetErase)
2207+
{
2208+
auto changeset_dump_dir_gen = get_changeset_dump_dir_generator(test_context);
2209+
auto server = Peer::create_server(test_context, changeset_dump_dir_gen.get());
2210+
auto client_1 = Peer::create_client(test_context, 2, changeset_dump_dir_gen.get());
2211+
auto client_2 = Peer::create_client(test_context, 3, changeset_dump_dir_gen.get());
2212+
2213+
// Baseline: insert one element in the set ('set' property)
2214+
client_1->create_schema([](WriteTransaction& tr) {
2215+
auto t = tr.get_group().add_table_with_primary_key("class_A", type_Int, "pk");
2216+
t->add_column_set(type_Int, "set");
2217+
auto obj = t->create_object_with_primary_key(1);
2218+
auto ss = obj.get_set<Int>("set");
2219+
ss.insert(42);
2220+
});
2221+
2222+
synchronize(server.get(), {client_1.get(), client_2.get()});
2223+
2224+
// Client 1 removes the element from the set
2225+
client_1->transaction([](Peer& p) {
2226+
auto ss = p.table("class_A")->begin()->get_set<Int>("set");
2227+
ss.erase(42);
2228+
});
2229+
2230+
// Client 2 removes the element from the set
2231+
client_2->transaction([](Peer& p) {
2232+
auto ss = p.table("class_A")->begin()->get_set<Int>("set");
2233+
ss.erase(42);
2234+
});
2235+
2236+
synchronize(server.get(), {client_1.get(), client_2.get()});
2237+
2238+
// Result: the set is empty
2239+
ReadTransaction read_server(server->shared_group);
2240+
ReadTransaction read_client_1(client_1->shared_group);
2241+
ReadTransaction read_client_2(client_2->shared_group);
2242+
CHECK(compare_groups(read_server, read_client_1));
2243+
CHECK(compare_groups(read_server, read_client_2, *test_context.logger));
2244+
auto table = read_server.get_table("class_A");
2245+
CHECK(table->begin()->get_set<Int>("set").is_empty());
2246+
}
2247+
22032248
TEST(Transform_ArrayEraseVsArrayErase)
22042249
{
22052250
// This test case recreates the problem that the above test exposes

0 commit comments

Comments
 (0)