• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

realm / realm-core / 1860

23 Nov 2023 05:57PM UTC coverage: 91.692% (+0.03%) from 91.66%
1860

push

Evergreen

web-flow
Client reset with recovery fixes (#7112)

* Do not resurrect objects that have been deleted by

the server when copying local lists with links in them.

* test insert in list recovery

* fix sort/distinct on LnkSet with unresolved links

* lint

92432 of 169288 branches covered (0.0%)

303 of 317 new or added lines in 8 files covered. (95.58%)

35 existing lines in 7 files now uncovered.

231734 of 252732 relevant lines covered (91.69%)

6882359.69 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.65
/src/realm/object_converter.cpp
1
/*************************************************************************
2
 *
3
 * Copyright 2022 Realm Inc.
4
 *
5
 * Licensed under the Apache License, Version 2.0 (the "License");
6
 * you may not use this file except in compliance with the License.
7
 * You may obtain a copy of the License at
8
 *
9
 * http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 *
17
 **************************************************************************/
18

19
#include <realm/object_converter.hpp>
20

21
#include <realm/dictionary.hpp>
22
#include <realm/list.hpp>
23
#include <realm/set.hpp>
24

25
#include <realm/util/flat_map.hpp>
26

27
namespace realm::converters {
28

29
// Takes two lists, src and dst, and makes dst equal src. src is unchanged.
30
void InterRealmValueConverter::copy_list(const Obj& src_obj, Obj& dst_obj, bool* update_out)
31
{
16,662✔
32
    // The two arrays are compared by finding the longest common prefix and
8,331✔
33
    // suffix.  The middle section differs between them and is made equal by
8,331✔
34
    // updating the middle section of dst.
8,331✔
35
    //
8,331✔
36
    // Example:
8,331✔
37
    // src = abcdefghi
8,331✔
38
    // dst = abcxyhi
8,331✔
39
    // The common prefix is abc. The common suffix is hi. xy is replaced by defg.
8,331✔
40
    LstBasePtr src = src_obj.get_listbase_ptr(m_src_col);
16,662✔
41
    LstBasePtr dst = dst_obj.get_listbase_ptr(m_dst_col);
16,662✔
42

8,331✔
43
    bool updated = false;
16,662✔
44
    const size_t len_src = src->size();
16,662✔
45
    const size_t len_dst_orig = dst->size();
16,662✔
46
    size_t len_min = std::min(len_src, len_dst_orig);
16,662✔
47

8,331✔
48
    size_t ndx = 0;
16,662✔
49
    size_t suffix_len = 0;
16,662✔
50

8,331✔
51
    while (ndx < len_min && cmp_src_to_dst(src->get_any(ndx), dst->get_any(ndx), nullptr, update_out) == 0) {
30,018✔
52
        ndx++;
13,356✔
53
    }
13,356✔
54

8,331✔
55
    if (ndx == len_src && len_src == len_dst_orig) {
16,662✔
56
        // all are equal, early out
6,381✔
57
        if (update_out) {
12,762✔
58
            *update_out = false;
6,726✔
59
        }
6,726✔
60
        return;
12,762✔
61
    }
12,762✔
62

1,950✔
63
    size_t suffix_len_max = len_min - ndx;
3,900✔
64

1,950✔
65
    while (suffix_len < suffix_len_max &&
4,638✔
66
           cmp_src_to_dst(src->get_any(len_src - 1 - suffix_len), dst->get_any(len_dst_orig - 1 - suffix_len),
3,408✔
67
                          nullptr, update_out) == 0) {
1,458✔
68
        suffix_len++;
738✔
69
    }
738✔
70

1,950✔
71
    len_min -= (ndx + suffix_len);
3,900✔
72

1,950✔
73
    auto dst_as_link_list = dynamic_cast<LnkLst*>(dst.get());
3,900✔
74
    auto dst_as_lst_mixed = dynamic_cast<Lst<Mixed>*>(dst.get());
3,900✔
75
    auto is_link_to_deleted_object = [&](const Mixed& src_value, const Mixed& converted_value) -> bool {
5,856✔
76
        return (dst_as_link_list && converted_value.is_null()) ||
5,856✔
77
               (dst_as_lst_mixed && converted_value.is_null() && src_value.is_type(type_TypedLink));
5,826✔
78
    };
5,856✔
79

1,950✔
80
    std::vector<size_t> dst_to_erase;
3,900✔
81
    for (size_t i = 0; i < len_min; i++) {
6,834✔
82
        InterRealmValueConverter::ConversionResult converted_src;
2,934✔
83
        const Mixed src_value = src->get_any(ndx);
2,934✔
84
        if (cmp_src_to_dst(src_value, dst->get_any(ndx), &converted_src, update_out)) {
2,934✔
85
            if (converted_src.requires_new_embedded_object) {
2,610✔
NEW
86
                REALM_ASSERT(dst_as_link_list); // this is the only type of list that supports embedded objects
×
NEW
87
                Obj embedded = dst_as_link_list->create_and_set_linked_object(ndx);
×
88
                track_new_embedded(converted_src.src_embedded_to_check, embedded);
×
89
            }
×
90
            else if (is_link_to_deleted_object(src_value, converted_src.converted_value)) {
2,610✔
91
                // this can happen when the source linked list points to an object
21✔
92
                // which has been deleted in the dest Realm. Lists do not support
21✔
93
                // setting an element to null, so it must be deleted later.
21✔
94
                dst_to_erase.push_back(ndx);
42✔
95
            }
42✔
96
            else {
2,568✔
97
                dst->set_any(ndx, converted_src.converted_value);
2,568✔
98
            }
2,568✔
99
            updated = true;
2,610✔
100
        }
2,610✔
101
        ndx++;
2,934✔
102
    }
2,934✔
103

1,950✔
104
    // New elements must be inserted in dst.
1,950✔
105
    while (ndx < len_src - suffix_len) {
7,578✔
106
        InterRealmValueConverter::ConversionResult converted_src;
3,678✔
107
        const Mixed src_value = src->get_any(ndx);
3,678✔
108
        cmp_src_to_dst(src_value, Mixed{}, &converted_src, update_out);
3,678✔
109
        size_t dst_ndx_to_insert = dst->size() - suffix_len;
3,678✔
110
        if (converted_src.requires_new_embedded_object) {
3,678✔
111
            REALM_ASSERT(dst_as_link_list); // this is the only type of list that supports embedded objects
432✔
112
            Obj embedded = dst_as_link_list->create_and_insert_linked_object(dst_ndx_to_insert);
432✔
113
            track_new_embedded(converted_src.src_embedded_to_check, embedded);
432✔
114
        }
432✔
115
        else if (is_link_to_deleted_object(src_value, converted_src.converted_value)) {
3,246✔
116
            // ignore trying to insert a link to a object which no longer exists
30✔
117
        }
60✔
118
        else {
3,186✔
119
            dst->insert_any(dst_ndx_to_insert, converted_src.converted_value);
3,186✔
120
        }
3,186✔
121
        ndx++;
3,678✔
122
        updated = true;
3,678✔
123
    }
3,678✔
124
    // Excess elements must be removed from ll_dst.
1,950✔
125
    if (dst->size() > len_src) {
3,900✔
126
        dst->remove(len_src - suffix_len, dst->size() - suffix_len);
1,062✔
127
        updated = true;
1,062✔
128
    }
1,062✔
129

1,950✔
130
    while (dst_to_erase.size()) {
3,942✔
131
        size_t ndx_to_remove = dst_to_erase.back();
42✔
132
        dst_as_link_list ? dst_as_link_list->remove(ndx_to_remove) : dst_as_lst_mixed->remove(ndx_to_remove);
33✔
133
        dst_to_erase.pop_back();
42✔
134
    }
42✔
135
    if (updated && update_out) {
3,900✔
136
        *update_out = updated;
1,884✔
137
    }
1,884✔
138
}
3,900✔
139

140
void InterRealmValueConverter::copy_set(const Obj& src_obj, Obj& dst_obj, bool* update_out)
141
{
11,118✔
142
    SetBasePtr src = src_obj.get_setbase_ptr(m_src_col);
11,118✔
143
    SetBasePtr dst = dst_obj.get_setbase_ptr(m_dst_col);
11,118✔
144

5,559✔
145
    std::vector<size_t> sorted_src, sorted_dst, to_insert, to_delete;
11,118✔
146
    constexpr bool ascending = true;
11,118✔
147
    // the implementation could be storing elements in sorted order, but
5,559✔
148
    // we don't assume that here.
5,559✔
149
    src->sort(sorted_src, ascending);
11,118✔
150
    dst->sort(sorted_dst, ascending);
11,118✔
151

5,559✔
152
    size_t dst_ndx = 0;
11,118✔
153
    size_t src_ndx = 0;
11,118✔
154
    while (src_ndx < sorted_src.size()) {
20,316✔
155
        if (dst_ndx == sorted_dst.size()) {
10,662✔
156
            // if we have reached the end of the dst items, all remaining
732✔
157
            // src items should be added
732✔
158
            while (src_ndx < sorted_src.size()) {
4,032✔
159
                to_insert.push_back(sorted_src[src_ndx++]);
2,568✔
160
            }
2,568✔
161
            break;
1,464✔
162
        }
1,464✔
163
        size_t ndx_in_src = sorted_src[src_ndx];
9,198✔
164
        Mixed src_val = src->get_any(ndx_in_src);
9,198✔
165
        while (dst_ndx < sorted_dst.size()) {
10,116✔
166
            size_t ndx_in_dst = sorted_dst[dst_ndx];
9,642✔
167

4,821✔
168
            int cmp = cmp_src_to_dst(src_val, dst->get_any(ndx_in_dst), nullptr, update_out);
9,642✔
169
            if (cmp == 0) {
9,642✔
170
                // equal: advance both src and dst
4,026✔
171
                ++dst_ndx;
8,052✔
172
                ++src_ndx;
8,052✔
173
                break;
8,052✔
174
            }
8,052✔
175
            else if (cmp < 0) {
1,590✔
176
                // src < dst: insert src, advance src only
336✔
177
                to_insert.push_back(ndx_in_src);
672✔
178
                ++src_ndx;
672✔
179
                break;
672✔
180
            }
672✔
181
            else {
918✔
182
                // src > dst: delete dst, advance only dst
459✔
183
                to_delete.push_back(ndx_in_dst);
918✔
184
                ++dst_ndx;
918✔
185
                continue;
918✔
186
            }
918✔
187
        }
9,642✔
188
    }
9,198✔
189
    while (dst_ndx < sorted_dst.size()) {
12,384✔
190
        to_delete.push_back(sorted_dst[dst_ndx++]);
1,266✔
191
    }
1,266✔
192

5,559✔
193
    std::sort(to_delete.begin(), to_delete.end());
11,118✔
194
    for (auto it = to_delete.rbegin(); it != to_delete.rend(); ++it) {
13,302✔
195
        dst->erase_any(dst->get_any(*it));
2,184✔
196
    }
2,184✔
197
    for (auto ndx : to_insert) {
7,179✔
198
        InterRealmValueConverter::ConversionResult converted_src;
3,240✔
199
        cmp_src_to_dst(src->get_any(ndx), Mixed{}, &converted_src, update_out);
3,240✔
200
        // we do not support a set of embedded objects
1,620✔
201
        REALM_ASSERT(!converted_src.requires_new_embedded_object);
3,240✔
202
        dst->insert_any(converted_src.converted_value);
3,240✔
203
    }
3,240✔
204

5,559✔
205
    if (update_out && (to_delete.size() || to_insert.size())) {
11,118✔
206
        *update_out = true;
1,938✔
207
    }
1,938✔
208
}
11,118✔
209

210
void InterRealmValueConverter::copy_dictionary(const Obj& src_obj, Obj& dst_obj, bool* update_out)
211
{
11,556✔
212
    Dictionary src = src_obj.get_dictionary(m_src_col);
11,556✔
213
    Dictionary dst = dst_obj.get_dictionary(m_dst_col);
11,556✔
214

5,778✔
215
    std::vector<size_t> to_insert, to_delete;
11,556✔
216

5,778✔
217
    size_t dst_ndx = 0;
11,556✔
218
    size_t src_ndx = 0;
11,556✔
219
    while (src_ndx < src.size()) {
23,586✔
220
        if (dst_ndx == dst.size()) {
13,200✔
221
            // if we have reached the end of the dst items, all remaining
585✔
222
            // src items should be added
585✔
223
            while (src_ndx < src.size()) {
3,084✔
224
                to_insert.push_back(src_ndx++);
1,914✔
225
            }
1,914✔
226
            break;
1,170✔
227
        }
1,170✔
228

6,015✔
229
        auto src_val = src.get_pair(src_ndx);
12,030✔
230
        while (dst_ndx < dst.size()) {
12,270✔
231
            auto dst_val = dst.get_pair(dst_ndx);
12,234✔
232
            int cmp = src_val.first.compare(dst_val.first);
12,234✔
233
            if (cmp == 0) {
12,234✔
234
                // Check if the values differ
5,592✔
235
                if (cmp_src_to_dst(src_val.second, dst_val.second, nullptr, update_out)) {
11,184✔
236
                    // values are different - modify destination, advance both
699✔
237
                    to_insert.push_back(src_ndx);
1,398✔
238
                }
1,398✔
239
                // keys and values equal: advance both src and dst
5,592✔
240
                ++dst_ndx;
11,184✔
241
                ++src_ndx;
11,184✔
242
                break;
11,184✔
243
            }
11,184✔
244
            else if (cmp < 0) {
1,050✔
245
                // src < dst: insert src, advance src only
405✔
246
                to_insert.push_back(src_ndx++);
810✔
247
                break;
810✔
248
            }
810✔
249
            else {
240✔
250
                // src > dst: delete dst, advance only dst
120✔
251
                to_delete.push_back(dst_ndx++);
240✔
252
                continue;
240✔
253
            }
240✔
254
        }
12,234✔
255
    }
12,030✔
256
    // at this point, we've gone through all src items but still have dst items
5,778✔
257
    // oustanding; these should all be deleted because they are not in src
5,778✔
258
    while (dst_ndx < dst.size()) {
12,060✔
259
        to_delete.push_back(dst_ndx++);
504✔
260
    }
504✔
261

5,778✔
262
    for (auto it = to_delete.rbegin(); it != to_delete.rend(); ++it) {
12,300✔
263
        dst.erase(dst.begin() + *it);
744✔
264
    }
744✔
265
    for (auto ndx : to_insert) {
7,839✔
266
        auto pair = src.get_pair(ndx);
4,122✔
267
        InterRealmValueConverter::ConversionResult converted_val;
4,122✔
268
        cmp_src_to_dst(pair.second, Mixed{}, &converted_val, update_out);
4,122✔
269
        if (converted_val.requires_new_embedded_object) {
4,122✔
270
            Obj new_embedded = dst.create_and_insert_linked_object(pair.first);
120✔
271
            track_new_embedded(converted_val.src_embedded_to_check, new_embedded);
120✔
272
        }
120✔
273
        else {
4,002✔
274
            dst.insert(pair.first, converted_val.converted_value);
4,002✔
275
        }
4,002✔
276
    }
4,122✔
277
    if (update_out && (to_delete.size() || to_insert.size())) {
11,556✔
278
        *update_out = true;
1,632✔
279
    }
1,632✔
280
}
11,556✔
281

282
void InterRealmValueConverter::copy_value(const Obj& src_obj, Obj& dst_obj, bool* update_out)
283
{
157,944✔
284
    if (m_src_col.is_list()) {
157,944✔
285
        copy_list(src_obj, dst_obj, update_out);
16,662✔
286
    }
16,662✔
287
    else if (m_src_col.is_dictionary()) {
141,282✔
288
        copy_dictionary(src_obj, dst_obj, update_out);
11,556✔
289
    }
11,556✔
290
    else if (m_src_col.is_set()) {
129,726✔
291
        copy_set(src_obj, dst_obj, update_out);
11,118✔
292
    }
11,118✔
293
    else {
118,608✔
294
        REALM_ASSERT(!m_src_col.is_collection());
118,608✔
295
        InterRealmValueConverter::ConversionResult converted_src;
118,608✔
296
        if (cmp_src_to_dst(src_obj.get_any(m_src_col), dst_obj.get_any(m_dst_col), &converted_src, update_out)) {
118,608✔
297
            if (converted_src.requires_new_embedded_object) {
70,857✔
298
                Obj new_embedded = dst_obj.create_and_set_linked_object(m_dst_col);
498✔
299
                track_new_embedded(converted_src.src_embedded_to_check, new_embedded);
498✔
300
            }
498✔
301
            else {
70,359✔
302
                dst_obj.set_any(m_dst_col, converted_src.converted_value);
70,359✔
303
            }
70,359✔
304
        }
70,857✔
305
    }
118,608✔
306
}
157,944✔
307

308

309
// If an embedded object is encountered, add it to a list of embedded objects to process.
310
// This relies on the property that embedded objects only have one incoming link
311
// otherwise there could be an infinite loop while discovering embedded objects.
312
void EmbeddedObjectConverter::track(const Obj& e_src, const Obj& e_dst)
313
{
67,158✔
314
    embedded_pending.push_back({e_src, e_dst});
67,158✔
315
}
67,158✔
316

317
void EmbeddedObjectConverter::process_pending()
318
{
32,304✔
319
    util::FlatMap<TableKey, InterRealmObjectConverter> converters;
32,304✔
320

16,152✔
321
    while (!embedded_pending.empty()) {
99,462✔
322
        EmbeddedToCheck pending = embedded_pending.back();
67,158✔
323
        embedded_pending.pop_back();
67,158✔
324

33,579✔
325
        TableRef dst_table = pending.embedded_in_dst.get_table();
67,158✔
326
        TableKey dst_table_key = dst_table->get_key();
67,158✔
327
        auto it = converters.find(dst_table_key);
67,158✔
328
        if (it == converters.end()) {
67,158✔
329
            TableRef src_table = pending.embedded_in_src.get_table();
1,074✔
330
            it = converters.insert({dst_table_key, InterRealmObjectConverter{src_table, dst_table, this}}).first;
1,074✔
331
        }
1,074✔
332
        InterRealmObjectConverter& converter = it->second;
67,158✔
333
        converter.copy(pending.embedded_in_src, pending.embedded_in_dst, nullptr);
67,158✔
334
    }
67,158✔
335
}
32,304✔
336

337
InterRealmValueConverter::InterRealmValueConverter(ConstTableRef src_table, ColKey src_col, ConstTableRef dst_table,
338
                                                   ColKey dst_col, EmbeddedObjectConverter* ec)
339
    : m_src_table(src_table)
340
    , m_dst_table(dst_table)
341
    , m_src_col(src_col)
342
    , m_dst_col(dst_col)
343
    , m_embedded_converter(ec)
344
    , m_is_embedded_link(false)
345
    , m_primitive_types_only(!(src_col.get_type() == col_type_TypedLink || src_col.get_type() == col_type_Link ||
346
                               src_col.get_type() == col_type_LinkList || src_col.get_type() == col_type_Mixed))
347
{
60,480✔
348
    if (!m_primitive_types_only) {
60,480✔
349
        REALM_ASSERT(src_table);
9,120✔
350
        m_opposite_of_src = src_table->get_opposite_table(src_col);
9,120✔
351
        m_opposite_of_dst = dst_table->get_opposite_table(dst_col);
9,120✔
352
        REALM_ASSERT(bool(m_opposite_of_src) == bool(m_opposite_of_dst));
9,120✔
353
        if (m_opposite_of_src) {
9,120✔
354
            m_is_embedded_link = m_opposite_of_src->is_embedded();
5,400✔
355
        }
5,400✔
356
    }
9,120✔
357
}
60,480✔
358

359
void InterRealmValueConverter::track_new_embedded(const Obj& src, const Obj& dst)
360
{
6,840✔
361
    m_embedded_converter->track(src, dst);
6,840✔
362
}
6,840✔
363

364
// convert `src` to the destination Realm and compare that value with `dst`
365
// If `converted_src_out` is provided, it will be set to the converted src value
366
int InterRealmValueConverter::cmp_src_to_dst(Mixed src, Mixed dst, ConversionResult* converted_src_out,
367
                                             bool* did_update_out)
368
{
170,868✔
369
    int cmp = 0;
170,868✔
370
    Mixed converted_src;
170,868✔
371
    if (m_primitive_types_only || !src.is_type(type_Link, type_TypedLink)) {
170,868✔
372
        converted_src = src;
154,794✔
373
        cmp = src.compare(dst);
154,794✔
374
    }
154,794✔
375
    else if (m_opposite_of_src) {
16,074✔
376
        ObjKey src_link_key = src.get<ObjKey>();
11,604✔
377
        if (m_is_embedded_link) {
11,604✔
378
            Obj src_embedded = m_opposite_of_src->get_object(src_link_key);
6,858✔
379
            REALM_ASSERT_DEBUG(src_embedded.is_valid());
6,858✔
380
            if (dst.is_type(type_Link, type_TypedLink)) {
6,858✔
381
                cmp = 0; // no need to set this link, there is already an embedded object here
5,790✔
382
                Obj dst_embedded = m_opposite_of_dst->get_object(dst.get<ObjKey>());
5,790✔
383
                REALM_ASSERT_DEBUG(dst_embedded.is_valid());
5,790✔
384
                converted_src = dst_embedded.get_key();
5,790✔
385
                track_new_embedded(src_embedded, dst_embedded);
5,790✔
386
            }
5,790✔
387
            else {
1,068✔
388
                cmp = src.compare(dst);
1,068✔
389
                if (converted_src_out) {
1,068✔
390
                    converted_src_out->requires_new_embedded_object = true;
1,050✔
391
                    converted_src_out->src_embedded_to_check = src_embedded;
1,050✔
392
                }
1,050✔
393
            }
1,068✔
394
        }
6,858✔
395
        else {
4,746✔
396
            Obj dst_link;
4,746✔
397
            if (m_opposite_of_dst == m_opposite_of_src) {
4,746✔
398
                // if this is the same Realm, we can use the ObjKey
96✔
399
                dst_link = m_opposite_of_dst->get_object(src_link_key);
192✔
400
            }
192✔
401
            else {
4,554✔
402
                // in different Realms we create a new object
2,277✔
403
                if (m_opposite_of_src->get_primary_key_column()) {
4,554✔
404
                    Mixed src_link_pk = m_opposite_of_src->get_primary_key(src_link_key);
4,554✔
405
                    dst_link =
4,554✔
406
                        m_opposite_of_dst->get_object_with_primary_key(src_link_pk); // returns ObjKey{} if not found
4,554✔
407
                }
4,554✔
408
                else {
×
409
                    dst_link = m_opposite_of_dst->create_object();
×
410
                }
×
411
            }
4,554✔
412
            converted_src = dst_link.get_key();
4,746✔
413
            if (dst.is_type(type_TypedLink)) {
4,746✔
414
                cmp = converted_src.compare(dst.get<ObjKey>());
3,780✔
415
            }
3,780✔
416
            else {
966✔
417
                cmp = converted_src.compare(dst);
966✔
418
            }
966✔
419
        }
4,746✔
420
    }
11,604✔
421
    else {
4,470✔
422
        ObjLink src_link = src.get<ObjLink>();
4,470✔
423
        if (src_link.is_unresolved()) {
4,470✔
424
            converted_src = Mixed{}; // no need to transfer over unresolved links
×
425
            cmp = converted_src.compare(dst);
×
426
        }
×
427
        else {
4,470✔
428
            TableRef src_link_table = m_src_table->get_parent_group()->get_table(src_link.get_table_key());
4,470✔
429
            REALM_ASSERT_EX(src_link_table, src_link.get_table_key());
4,470✔
430
            TableRef dst_link_table = m_dst_table->get_parent_group()->get_table(src_link_table->get_name());
4,470✔
431
            REALM_ASSERT_EX(dst_link_table, src_link_table->get_name());
4,470✔
432
            // embedded tables should always be covered by the m_opposite_of_src case above.
2,235✔
433
            REALM_ASSERT_EX(!src_link_table->is_embedded(), src_link_table->get_name());
4,470✔
434
            // regular table, convert by pk
2,235✔
435
            if (src_link_table->get_primary_key_column()) {
4,470✔
436
                Mixed src_pk = src_link_table->get_primary_key(src_link.get_obj_key());
4,470✔
437
                if (Obj dst_link = dst_link_table->get_object_with_primary_key(src_pk)) {
4,470✔
438
                    converted_src = ObjLink{dst_link_table->get_key(), dst_link.get_key()};
4,398✔
439
                }
4,398✔
440
            }
4,470✔
441
            else if (src_link_table == dst_link_table) {
×
442
                // no pk, but this is the same Realm, so convert by ObjKey
443
                Obj dst_link = dst_link_table->get_object(src_link.get_obj_key());
×
444
                converted_src = ObjLink{dst_link_table->get_key(), dst_link.get_key()};
×
445
            }
×
446
            else {
×
447
                // no pk, and different Realm, create an object
448
                Obj dst_link = dst_link_table->create_object();
×
449
                converted_src = ObjLink{dst_link_table->get_key(), dst_link.get_key()};
×
450
            }
×
451
            cmp = converted_src.compare(dst);
4,470✔
452
        }
4,470✔
453
    }
4,470✔
454
    if (converted_src_out) {
170,868✔
455
        converted_src_out->converted_value = converted_src;
132,582✔
456
    }
132,582✔
457
    if (did_update_out && cmp) {
170,868✔
458
        *did_update_out = true;
16,515✔
459
    }
16,515✔
460
    return cmp;
170,868✔
461
}
170,868✔
462

463
InterRealmObjectConverter::InterRealmObjectConverter(ConstTableRef table_src, TableRef table_dst,
464
                                                     EmbeddedObjectConverter* embedded_tracker)
465
    : m_embedded_tracker(embedded_tracker)
466
{
26,262✔
467
    m_columns_cache.reserve(table_src->get_column_count());
26,262✔
468
    ColKey pk_col = table_src->get_primary_key_column();
26,262✔
469
    for (ColKey col_key_src : table_src->get_column_keys()) {
84,312✔
470
        if (col_key_src == pk_col)
84,312✔
471
            continue;
25,188✔
472
        StringData col_name = table_src->get_column_name(col_key_src);
59,124✔
473
        ColKey col_key_dst = table_dst->get_column_key(col_name);
59,124✔
474
        REALM_ASSERT(col_key_dst);
59,124✔
475
        m_columns_cache.emplace_back(table_src, col_key_src, table_dst, col_key_dst, m_embedded_tracker);
59,124✔
476
    }
59,124✔
477
}
26,262✔
478

479
void InterRealmObjectConverter::copy(const Obj& src, Obj& dst, bool* update_out)
480
{
103,872✔
481
    for (auto& column : m_columns_cache) {
156,588✔
482
        column.copy_value(src, dst, update_out);
156,588✔
483
    }
156,588✔
484
}
103,872✔
485

486
} // namespace realm::converters
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc