Skip to content

Commit 94bbeef

Browse files
committed
Fix Transaction::copy_to() for nested collection
1 parent 12d53fc commit 94bbeef

File tree

5 files changed

+44
-28
lines changed

5 files changed

+44
-28
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
* Catch2 is no longer required as a submodule if the `REALM_NO_TESTS` flag is set.
5353
* Sha-2 is no longer required as a submodule on Windows if linking with OpenSSL.
5454
* The Catch2 submodule has moved to `test/external/catch`.
55+
* Fix possible file corruption if using Transaction::copy_to if nested collections are present.
5556

5657
----------------------------------------------
5758

src/realm/impl/copy_replication.cpp

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -130,12 +130,15 @@ void CopyReplication::list_clear(const CollectionBase& coll)
130130
m_current.obj_in_destination.get_listbase_ptr(dest_col_key)->clear();
131131
}
132132

133-
void CopyReplication::list_insert(const CollectionBase& coll, size_t idx, Mixed value, size_t)
133+
void CopyReplication::list_insert(const CollectionBase& source_coll, size_t idx, Mixed value, size_t)
134134
{
135-
ColKey col_key = coll.get_col_key();
136-
sync(coll);
135+
ColKey col_key = source_coll.get_col_key();
136+
sync(source_coll);
137137
auto dest_col_key = get_colkey_in_destination_realm(col_key);
138-
auto list = m_current.obj_in_destination.get_listbase_ptr(dest_col_key);
138+
auto path = source_coll.get_short_path();
139+
path[0] = dest_col_key;
140+
auto coll = m_current.obj_in_destination.get_collection_ptr(path);
141+
auto list = dynamic_cast<LstBase*>(coll.get());
139142
if (value.is_type(type_Link, type_TypedLink)) {
140143
value = handle_link(col_key, value, [&](TableRef) {
141144
auto link_list = m_current.obj_in_destination.get_linklist(dest_col_key);
@@ -162,31 +165,34 @@ void CopyReplication::set_insert(const CollectionBase& coll, size_t, Mixed value
162165
set->insert_any(value);
163166
}
164167

165-
void CopyReplication::dictionary_insert(const CollectionBase& coll, size_t, Mixed key, Mixed value)
168+
void CopyReplication::dictionary_insert(const CollectionBase& source_coll, size_t, Mixed key, Mixed value)
166169
{
167-
ColKey col_key = coll.get_col_key();
168-
sync(coll);
170+
ColKey col_key = source_coll.get_col_key();
171+
sync(source_coll);
169172
auto dest_col_key = get_colkey_in_destination_realm(col_key);
170-
auto dict = m_current.obj_in_destination.get_dictionary(dest_col_key);
173+
auto path = source_coll.get_short_path();
174+
path[0] = dest_col_key;
175+
auto coll = m_current.obj_in_destination.get_collection_ptr(path);
176+
auto dict = dynamic_cast<Dictionary*>(coll.get());
171177
if (value.is_type(type_Link, type_TypedLink)) {
172178
value = handle_link(col_key, value, [&](TableRef dest_target_table) {
173179
// Check if dictionary obj has embedded obj already
174-
size_t ndx = dict.find_any_key(key);
180+
size_t ndx = dict->find_any_key(key);
175181
if (ndx != realm::not_found) {
176-
auto val = dict.get_any(ndx);
182+
auto val = dict->get_any(ndx);
177183
if (val.is_type(type_Link)) {
178184
ObjKey key = val.get<ObjKey>();
179185
m_current.obj_in_destination = dest_target_table->get_object(key);
180186
return;
181187
}
182188
}
183189
// If not, create one
184-
m_current.obj_in_destination = dict.create_and_insert_linked_object(key);
190+
m_current.obj_in_destination = dict->create_and_insert_linked_object(key);
185191
});
186192
if (value.is_null())
187193
return;
188194
}
189-
dict.insert(key, value);
195+
dict->insert(key, value);
190196
}
191197

192198
void CopyReplication::sync(const Table* t, ObjKey obj_key)

src/realm/obj.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -240,12 +240,12 @@ bool Obj::compare_list_in_mixed(Lst<Mixed>& val1, Lst<Mixed>& val2, ColKey ck, O
240240
auto m2 = val2.get_any(i);
241241

242242
if (m1.is_type(type_List) && m2.is_type(type_List)) {
243-
DummyParent parent(get_table(), m2.get_ref());
243+
DummyParent parent(other.get_table(), m2.get_ref());
244244
Lst<Mixed> list(parent, 0);
245245
return compare_list_in_mixed(*val1.get_list(i), list, ck, other, col_name);
246246
}
247247
else if (m1.is_type(type_Dictionary) && m2.is_type(type_Dictionary)) {
248-
DummyParent parent(get_table(), m2.get_ref());
248+
DummyParent parent(other.get_table(), m2.get_ref());
249249
Dictionary dict(parent, 0);
250250
return compare_dict_in_mixed(*val1.get_dictionary(i), dict, ck, other, col_name);
251251
}
@@ -269,12 +269,12 @@ bool Obj::compare_dict_in_mixed(Dictionary& val1, Dictionary& val2, ColKey ck, O
269269
return false;
270270

271271
if (m1.is_type(type_List) && m2.is_type(type_List)) {
272-
DummyParent parent(get_table(), m2.get_ref());
272+
DummyParent parent(other.get_table(), m2.get_ref());
273273
Lst<Mixed> list(parent, 0);
274274
return compare_list_in_mixed(*val1.get_list(k1.get_string()), list, ck, other, col_name);
275275
}
276276
else if (m1.is_type(type_Dictionary) && m2.is_type(type_Dictionary)) {
277-
DummyParent parent(get_table(), m2.get_ref());
277+
DummyParent parent(other.get_table(), m2.get_ref());
278278
Dictionary dict(parent, 0);
279279
return compare_dict_in_mixed(*val1.get_dictionary(k1.get_string()), dict, ck, other, col_name);
280280
}

src/realm/transaction.cpp

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -46,23 +46,27 @@ ColInfo get_col_info(const Table* table)
4646
}
4747

4848
void add_list_to_repl(CollectionBase& list, Replication& repl, util::UniqueFunction<void(Mixed)> update_embedded);
49+
4950
void add_dictionary_to_repl(Dictionary& dict, Replication& repl, util::UniqueFunction<void(Mixed)> update_embedded)
5051
{
5152
size_t sz = dict.size();
5253
for (size_t n = 0; n < sz; ++n) {
5354
const auto& [key, val] = dict.get_pair(n);
54-
repl.dictionary_insert(dict, n, key, val);
5555
if (val.is_type(type_List)) {
56+
repl.dictionary_insert(dict, n, key, Mixed{0, CollectionType::List});
5657
auto n_list = dict.get_list({key.get_string()});
57-
add_list_to_repl(*n_list, *dict.get_table()->get_repl(), nullptr);
58+
add_list_to_repl(*n_list, repl, nullptr);
5859
}
5960
else if (val.is_type(type_Dictionary)) {
60-
repl.dictionary_insert(dict, n, key, val);
61+
repl.dictionary_insert(dict, n, key, Mixed{0, CollectionType::Dictionary});
6162
auto n_dict = dict.get_dictionary({key.get_string()});
62-
add_dictionary_to_repl(*n_dict, *dict.get_table()->get_repl(), nullptr);
63+
add_dictionary_to_repl(*n_dict, repl, nullptr);
6364
}
64-
else if (update_embedded) {
65-
update_embedded(val);
65+
else {
66+
repl.dictionary_insert(dict, n, key, val);
67+
if (update_embedded) {
68+
update_embedded(val);
69+
}
6670
}
6771
}
6872
}
@@ -72,17 +76,21 @@ void add_list_to_repl(CollectionBase& list, Replication& repl, util::UniqueFunct
7276
auto sz = list.size();
7377
for (size_t n = 0; n < sz; n++) {
7478
auto val = list.get_any(n);
75-
repl.list_insert(list, n, val, n);
7679
if (val.is_type(type_List)) {
80+
repl.list_insert(list, n, Mixed{0, CollectionType::List}, n);
7781
auto n_list = list.get_list({n});
78-
add_list_to_repl(*n_list, *list.get_table()->get_repl(), nullptr);
82+
add_list_to_repl(*n_list, repl, nullptr);
7983
}
8084
else if (val.is_type(type_Dictionary)) {
85+
repl.list_insert(list, n, Mixed{0, CollectionType::Dictionary}, n);
8186
auto n_dict = list.get_dictionary({n});
82-
add_dictionary_to_repl(*n_dict, *list.get_table()->get_repl(), nullptr);
87+
add_dictionary_to_repl(*n_dict, repl, nullptr);
8388
}
84-
else if (update_embedded) {
85-
update_embedded(val);
89+
else {
90+
repl.list_insert(list, n, val, n);
91+
if (update_embedded) {
92+
update_embedded(val);
93+
}
8694
}
8795
}
8896
}

test/test_shared.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4278,7 +4278,6 @@ TEST(Shared_WriteTo)
42784278
tr->commit_and_continue_as_read();
42794279
// tr->to_json(std::cout);
42804280

4281-
42824281
// Create remote db
42834282
DBRef db2 = DB::create(make_in_realm_history(), path2);
42844283
{
@@ -4310,7 +4309,9 @@ TEST(Shared_WriteTo)
43104309
// Copy local object over
43114310
auto dest = db2->start_write();
43124311
tr->copy_to(dest);
4312+
dest->verify();
43134313
dest->commit_and_continue_as_read();
4314+
// dest->to_json(std::cout);
43144315

43154316
// The difference between the two realms should now be that the remote db has an
43164317
// extra baa object with pk 333.

0 commit comments

Comments
 (0)