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

realm / realm-core / github_pull_request_281750

30 Oct 2023 03:37PM UTC coverage: 90.528% (-1.0%) from 91.571%
github_pull_request_281750

Pull #6073

Evergreen

jedelbo
Log free space and history sizes when opening file
Pull Request #6073: Merge next-major

95488 of 175952 branches covered (0.0%)

8973 of 12277 new or added lines in 149 files covered. (73.09%)

622 existing lines in 51 files now uncovered.

233503 of 257934 relevant lines covered (90.53%)

6533720.56 hits per line

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

58.92
/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
namespace realm::converters {
27

28
// Takes two lists, src and dst, and makes dst equal src. src is unchanged.
29
void InterRealmValueConverter::copy_list(const LstBase& src, LstBase& dst, bool* update_out) const
30
{
16,236✔
31
    // The two arrays are compared by finding the longest common prefix and
8,118✔
32
    // suffix.  The middle section differs between them and is made equal by
8,118✔
33
    // updating the middle section of dst.
8,118✔
34
    //
8,118✔
35
    // Example:
8,118✔
36
    // src = abcdefghi
8,118✔
37
    // dst = abcxyhi
8,118✔
38
    // The common prefix is abc. The common suffix is hi. xy is replaced by defg.
8,118✔
39

8,118✔
40
    bool updated = false;
16,236✔
41
    size_t len_src = src.size();
16,236✔
42
    size_t len_dst = dst.size();
16,236✔
43
    size_t len_min = std::min(len_src, len_dst);
16,236✔
44

8,118✔
45
    size_t ndx = 0;
16,236✔
46
    size_t suffix_len = 0;
16,236✔
47

8,118✔
48
    while (ndx < len_min && cmp_src_to_dst(src.get_any(ndx), dst.get_any(ndx), nullptr, update_out) == 0) {
29,070✔
49
        ndx++;
12,834✔
50
    }
12,834✔
51

8,118✔
52
    size_t suffix_len_max = len_min - ndx;
16,236✔
53

8,118✔
54
    while (suffix_len < suffix_len_max &&
16,818✔
55
           cmp_src_to_dst(src.get_any(len_src - 1 - suffix_len), dst.get_any(len_dst - 1 - suffix_len), nullptr,
9,339✔
56
                          update_out) == 0) {
1,221✔
57
        suffix_len++;
582✔
58
    }
582✔
59

8,118✔
60
    len_min -= (ndx + suffix_len);
16,236✔
61

8,118✔
62
    for (size_t i = 0; i < len_min; i++) {
18,840✔
63
        InterRealmValueConverter::ConversionResult converted_src;
2,604✔
64
        if (cmp_src_to_dst(src.get_any(ndx), dst.get_any(ndx), &converted_src, update_out)) {
2,604✔
65
            if (converted_src.requires_new_embedded_object) {
2,316✔
NEW
66
                auto lnklist = dynamic_cast<LnkLst*>(&dst);
×
67
                REALM_ASSERT(lnklist); // this is the only type of list that supports embedded objects
×
68
                Obj embedded = lnklist->create_and_set_linked_object(ndx);
×
69
                track_new_embedded(converted_src.src_embedded_to_check, embedded);
×
70
            }
×
71
            else {
2,316✔
72
                dst.set_any(ndx, converted_src.converted_value);
2,316✔
73
            }
2,316✔
74
            updated = true;
2,316✔
75
        }
2,316✔
76
        ndx++;
2,604✔
77
    }
2,604✔
78

8,118✔
79
    // New elements must be inserted in dst.
8,118✔
80
    while (len_dst < len_src) {
19,716✔
81
        InterRealmValueConverter::ConversionResult converted_src;
3,480✔
82
        cmp_src_to_dst(src.get_any(ndx), Mixed{}, &converted_src, update_out);
3,480✔
83
        if (converted_src.requires_new_embedded_object) {
3,480✔
84
            auto lnklist = dynamic_cast<LnkLst*>(&dst);
432✔
85
            REALM_ASSERT(lnklist); // this is the only type of list that supports embedded objects
432✔
86
            Obj embedded = lnklist->create_and_insert_linked_object(ndx);
432✔
87
            track_new_embedded(converted_src.src_embedded_to_check, embedded);
432✔
88
        }
432✔
89
        else {
3,048✔
90
            dst.insert_any(ndx, converted_src.converted_value);
3,048✔
91
        }
3,048✔
92
        len_dst++;
3,480✔
93
        ndx++;
3,480✔
94
        updated = true;
3,480✔
95
    }
3,480✔
96
    // Excess elements must be removed from ll_dst.
8,118✔
97
    if (len_dst > len_src) {
16,236✔
98
        dst.remove(len_src - suffix_len, len_dst - suffix_len);
972✔
99
        updated = true;
972✔
100
    }
972✔
101

8,118✔
102
    REALM_ASSERT(dst.size() == len_src);
16,236✔
103
    if (updated && update_out) {
16,236✔
104
        *update_out = updated;
1,650✔
105
    }
1,650✔
106
}
16,236✔
107

108
void InterRealmValueConverter::copy_set(const SetBase& src, SetBase& dst, bool* update_out) const
109
{
10,902✔
110
    std::vector<size_t> sorted_src, sorted_dst, to_insert, to_delete;
10,902✔
111
    constexpr bool ascending = true;
10,902✔
112
    // the implementation could be storing elements in sorted order, but
5,451✔
113
    // we don't assume that here.
5,451✔
114
    src.sort(sorted_src, ascending);
10,902✔
115
    dst.sort(sorted_dst, ascending);
10,902✔
116

5,451✔
117
    size_t dst_ndx = 0;
10,902✔
118
    size_t src_ndx = 0;
10,902✔
119
    while (src_ndx < sorted_src.size()) {
19,692✔
120
        if (dst_ndx == sorted_dst.size()) {
10,170✔
121
            // if we have reached the end of the dst items, all remaining
690✔
122
            // src items should be added
690✔
123
            while (src_ndx < sorted_src.size()) {
3,816✔
124
                to_insert.push_back(sorted_src[src_ndx++]);
2,436✔
125
            }
2,436✔
126
            break;
1,380✔
127
        }
1,380✔
128
        size_t ndx_in_src = sorted_src[src_ndx];
8,790✔
129
        Mixed src_val = src.get_any(ndx_in_src);
8,790✔
130
        while (dst_ndx < sorted_dst.size()) {
9,540✔
131
            size_t ndx_in_dst = sorted_dst[dst_ndx];
9,114✔
132

4,557✔
133
            int cmp = cmp_src_to_dst(src_val, dst.get_any(ndx_in_dst), nullptr, update_out);
9,114✔
134
            if (cmp == 0) {
9,114✔
135
                // equal: advance both src and dst
3,852✔
136
                ++dst_ndx;
7,704✔
137
                ++src_ndx;
7,704✔
138
                break;
7,704✔
139
            }
7,704✔
140
            else if (cmp < 0) {
1,410✔
141
                // src < dst: insert src, advance src only
330✔
142
                to_insert.push_back(ndx_in_src);
660✔
143
                ++src_ndx;
660✔
144
                break;
660✔
145
            }
660✔
146
            else {
750✔
147
                // src > dst: delete dst, advance only dst
375✔
148
                to_delete.push_back(ndx_in_dst);
750✔
149
                ++dst_ndx;
750✔
150
                continue;
750✔
151
            }
750✔
152
        }
9,114✔
153
    }
8,790✔
154
    while (dst_ndx < sorted_dst.size()) {
12,132✔
155
        to_delete.push_back(sorted_dst[dst_ndx++]);
1,230✔
156
    }
1,230✔
157

5,451✔
158
    std::sort(to_delete.begin(), to_delete.end());
10,902✔
159
    for (auto it = to_delete.rbegin(); it != to_delete.rend(); ++it) {
12,882✔
160
        dst.erase_any(dst.get_any(*it));
1,980✔
161
    }
1,980✔
162
    for (auto ndx : to_insert) {
6,999✔
163
        InterRealmValueConverter::ConversionResult converted_src;
3,096✔
164
        cmp_src_to_dst(src.get_any(ndx), Mixed{}, &converted_src, update_out);
3,096✔
165
        // we do not support a set of embedded objects
1,548✔
166
        REALM_ASSERT(!converted_src.requires_new_embedded_object);
3,096✔
167
        dst.insert_any(converted_src.converted_value);
3,096✔
168
    }
3,096✔
169

5,451✔
170
    if (update_out && (to_delete.size() || to_insert.size())) {
10,902✔
171
        *update_out = true;
1,782✔
172
    }
1,782✔
173
}
10,902✔
174

175
void InterRealmValueConverter::copy_dictionary(const Dictionary& src, Dictionary& dst, bool* update_out) const
176
{
11,364✔
177
    std::vector<size_t> to_insert, to_delete;
11,364✔
178

5,682✔
179
    size_t dst_ndx = 0;
11,364✔
180
    size_t src_ndx = 0;
11,364✔
181
    while (src_ndx < src.size()) {
22,746✔
182
        if (dst_ndx == dst.size()) {
12,504✔
183
            // if we have reached the end of the dst items, all remaining
561✔
184
            // src items should be added
561✔
185
            while (src_ndx < src.size()) {
2,988✔
186
                to_insert.push_back(src_ndx++);
1,866✔
187
            }
1,866✔
188
            break;
1,122✔
189
        }
1,122✔
190

5,691✔
191
        auto src_val = src.get_pair(src_ndx);
11,382✔
192
        while (dst_ndx < dst.size()) {
11,574✔
193
            auto dst_val = dst.get_pair(dst_ndx);
11,562✔
194
            int cmp = src_val.first.compare(dst_val.first);
11,562✔
195
            if (cmp == 0) {
11,562✔
196
                // Check if the values differ
5,280✔
197
                if (cmp_src_to_dst(src_val.second, dst_val.second, nullptr, update_out)) {
10,560✔
198
                    // values are different - modify destination, advance both
687✔
199
                    to_insert.push_back(src_ndx);
1,374✔
200
                }
1,374✔
201
                // keys and values equal: advance both src and dst
5,280✔
202
                ++dst_ndx;
10,560✔
203
                ++src_ndx;
10,560✔
204
                break;
10,560✔
205
            }
10,560✔
206
            else if (cmp < 0) {
1,002✔
207
                // src < dst: insert src, advance src only
405✔
208
                to_insert.push_back(src_ndx++);
810✔
209
                break;
810✔
210
            }
810✔
211
            else {
192✔
212
                // src > dst: delete dst, advance only dst
96✔
213
                to_delete.push_back(dst_ndx++);
192✔
214
                continue;
192✔
215
            }
192✔
216
        }
11,562✔
217
    }
11,382✔
218
    // at this point, we've gone through all src items but still have dst items
5,682✔
219
    // oustanding; these should all be deleted because they are not in src
5,682✔
220
    while (dst_ndx < dst.size()) {
11,772✔
221
        to_delete.push_back(dst_ndx++);
408✔
222
    }
408✔
223

5,682✔
224
    for (auto it = to_delete.rbegin(); it != to_delete.rend(); ++it) {
11,964✔
225
        dst.erase(dst.begin() + *it);
600✔
226
    }
600✔
227
    for (auto ndx : to_insert) {
7,707✔
228
        auto pair = src.get_pair(ndx);
4,050✔
229
        InterRealmValueConverter::ConversionResult converted_val;
4,050✔
230
        cmp_src_to_dst(pair.second, Mixed{}, &converted_val, update_out);
4,050✔
231
        if (converted_val.requires_new_embedded_object) {
4,050✔
232
            Obj new_embedded = dst.create_and_insert_linked_object(pair.first);
120✔
233
            track_new_embedded(converted_val.src_embedded_to_check, new_embedded);
120✔
234
        }
120✔
235
        else {
3,930✔
236
            dst.insert(pair.first, converted_val.converted_value);
3,930✔
237
        }
3,930✔
238
    }
4,050✔
239
    if (update_out && (to_delete.size() || to_insert.size())) {
11,364✔
240
        *update_out = true;
1,500✔
241
    }
1,500✔
242
}
11,364✔
243

244
void InterRealmValueConverter::copy_value(const Obj& src_obj, Obj& dst_obj, bool* update_out)
245
{
152,496✔
246
    if (m_src_col.is_list()) {
152,496✔
247
        LstBasePtr src = src_obj.get_listbase_ptr(m_src_col);
16,236✔
248
        LstBasePtr dst = dst_obj.get_listbase_ptr(m_dst_col);
16,236✔
249
        copy_list(*src, *dst, update_out);
16,236✔
250
    }
16,236✔
251
    else if (m_src_col.is_dictionary()) {
136,260✔
252
        Dictionary src = src_obj.get_dictionary(m_src_col);
11,364✔
253
        Dictionary dst = dst_obj.get_dictionary(m_dst_col);
11,364✔
254
        copy_dictionary(src, dst, update_out);
11,364✔
255
    }
11,364✔
256
    else if (m_src_col.is_set()) {
124,896✔
257
        SetBasePtr src = src_obj.get_setbase_ptr(m_src_col);
10,902✔
258
        SetBasePtr dst = dst_obj.get_setbase_ptr(m_dst_col);
10,902✔
259
        copy_set(*src, *dst, update_out);
10,902✔
260
    }
10,902✔
261
    else {
113,994✔
262
        REALM_ASSERT(!m_src_col.is_collection());
113,994✔
263
        // nested collections
56,997✔
264
        auto src_mixed = src_obj.get_any(m_src_col);
113,994✔
265
        if (src_mixed.is_type(type_List)) {
113,994✔
NEW
266
            dst_obj.set_collection(m_dst_col, CollectionType::List);
×
NEW
267
            Lst<Mixed> src_list{src_obj, m_src_col};
×
NEW
268
            Lst<Mixed> dst_list{dst_obj, m_dst_col};
×
NEW
269
            handle_list_in_mixed(src_list, dst_list);
×
NEW
270
        }
×
271
        else if (src_mixed.is_type(type_Set)) {
113,994✔
NEW
272
            dst_obj.set_collection(m_dst_col, CollectionType::Set);
×
NEW
273
            realm::Set<Mixed> src_set{src_obj, m_src_col};
×
NEW
274
            realm::Set<Mixed> dst_set{dst_obj, m_dst_col};
×
275
            // sets cannot be nested, so we just need to copy the values
NEW
276
            copy_set(src_set, dst_set, nullptr);
×
NEW
277
        }
×
278
        else if (src_mixed.is_type(type_Dictionary)) {
113,994✔
NEW
279
            dst_obj.set_collection(m_dst_col, CollectionType::Dictionary);
×
NEW
280
            Dictionary src_dict{src_obj, m_src_col};
×
NEW
281
            Dictionary dst_dict{dst_obj, m_dst_col};
×
NEW
282
            handle_dictionary_in_mixed(src_dict, dst_dict);
×
NEW
283
        }
×
284
        else {
113,994✔
285
            InterRealmValueConverter::ConversionResult converted_src;
113,994✔
286
            auto dst_mixed = dst_obj.get_any(m_dst_col);
113,994✔
287
            if (cmp_src_to_dst(src_mixed, dst_mixed, &converted_src, update_out)) {
113,994✔
288
                if (converted_src.requires_new_embedded_object) {
70,308✔
289
                    Obj new_embedded = dst_obj.create_and_set_linked_object(m_dst_col);
498✔
290
                    track_new_embedded(converted_src.src_embedded_to_check, new_embedded);
498✔
291
                }
498✔
292
                else {
69,810✔
293
                    dst_obj.set_any(m_dst_col, converted_src.converted_value);
69,810✔
294
                }
69,810✔
295
            }
70,308✔
296
        }
113,994✔
297
    }
113,994✔
298
}
152,496✔
299

300
//
301
// Handle collections in mixed. A collection can have N nested levels (expect for Sets). And these levels can be
302
// nested in arbitrary way (eg a List within a Dictionary or viceversa). In order to try to merge server changes with
303
// client changes, the algorithm needs to go throw each single element in the collection, check its type and perform
304
// the most appropriate action in order to miminize the number of notificiations triggered.
305
//
306
void InterRealmValueConverter::handle_list_in_mixed(const Lst<Mixed>& src_list, Lst<Mixed>& dst_list) const
NEW
307
{
×
NEW
308
    int sz = (int)std::min(src_list.size(), dst_list.size());
×
NEW
309
    int left = 0;
×
NEW
310
    int right = (int)sz - 1;
×
311

312
    // find fist not matching element from beginning
NEW
313
    while (left < sz) {
×
NEW
314
        auto src_any = src_list.get_any(left);
×
NEW
315
        auto dst_any = dst_list.get_any(left);
×
NEW
316
        if (src_any != dst_any)
×
NEW
317
            break;
×
NEW
318
        if (is_collection(src_any) && !check_matching_list(src_list, dst_list, left, to_collection_type(src_any)))
×
NEW
319
            break;
×
NEW
320
        left += 1;
×
NEW
321
    }
×
322

323
    // find first not matching element from end
NEW
324
    while (right >= 0) {
×
NEW
325
        auto src_any = src_list.get_any(right);
×
NEW
326
        auto dst_any = dst_list.get_any(right);
×
NEW
327
        if (src_any != dst_any)
×
NEW
328
            break;
×
NEW
329
        if (is_collection(src_any) && !check_matching_list(src_list, dst_list, right, to_collection_type(src_any)))
×
NEW
330
            break;
×
NEW
331
        right -= 1;
×
NEW
332
    }
×
333

334
    // Replace all different elements in [left, right]
NEW
335
    while (left <= right) {
×
NEW
336
        auto src_any = src_list.get_any(left);
×
NEW
337
        auto dst_any = dst_list.get_any(left);
×
338

NEW
339
        if (is_collection(src_any)) {
×
NEW
340
            auto coll_type = to_collection_type(src_any);
×
341

NEW
342
            if (!dst_any.is_type(src_any.get_type())) {
×
343
                // Mixed vs Collection
NEW
344
                dst_list.set_collection(left, coll_type);
×
NEW
345
                copy_list_in_mixed(src_list, dst_list, left, coll_type);
×
NEW
346
            }
×
NEW
347
            else if (!check_matching_list(src_list, dst_list, left, coll_type)) {
×
348
                // Collection vs Collection
NEW
349
                dst_list.set_any(left, src_any);
×
NEW
350
                copy_list_in_mixed(src_list, dst_list, left, coll_type);
×
UNCOV
351
            }
×
UNCOV
352
        }
×
NEW
353
        else if (dst_any != src_any) {
×
354
            // Mixed vs Mixed
NEW
355
            dst_list.set_any(left, src_any);
×
NEW
356
        }
×
NEW
357
        left += 1;
×
NEW
358
    }
×
359

360
    // remove dst elements not present in src
NEW
361
    if (dst_list.size() > src_list.size()) {
×
NEW
362
        auto dst_size = dst_list.size();
×
NEW
363
        auto src_size = src_list.size();
×
NEW
364
        while (dst_size > src_size)
×
NEW
365
            dst_list.remove(--dst_size);
×
NEW
366
    }
×
367

368
    // append remainig src into dst
NEW
369
    for (size_t i = dst_list.size(); i < src_list.size(); ++i) {
×
NEW
370
        auto src_any = src_list.get(i);
×
NEW
371
        if (is_collection(src_any)) {
×
NEW
372
            auto coll_type = to_collection_type(src_any);
×
NEW
373
            dst_list.insert_collection(i, coll_type);
×
NEW
374
            copy_list_in_mixed(src_list, dst_list, i, coll_type);
×
NEW
375
        }
×
NEW
376
        else {
×
NEW
377
            dst_list.insert_any(i, src_any);
×
NEW
378
        }
×
UNCOV
379
    }
×
UNCOV
380
}
×
381

382
void InterRealmValueConverter::handle_dictionary_in_mixed(Dictionary& src_dictionary,
383
                                                          Dictionary& dst_dictionary) const
NEW
384
{
×
NEW
385
    std::vector<size_t> to_insert, to_delete;
×
NEW
386
    size_t src_ndx = 0, dst_ndx = 0;
×
NEW
387
    while (src_ndx < src_dictionary.size() && dst_ndx < dst_dictionary.size()) {
×
NEW
388
        const auto [key_src, src_any] = src_dictionary.get_pair(src_ndx);
×
NEW
389
        const auto [key_dst, dst_any] = dst_dictionary.get_pair(dst_ndx);
×
390

NEW
391
        auto cmp = key_src.compare(key_dst);
×
NEW
392
        if (cmp == 0) {
×
NEW
393
            if (src_any != dst_any) {
×
NEW
394
                to_insert.push_back(src_ndx);
×
NEW
395
            }
×
NEW
396
            else if (is_collection(src_any) &&
×
NEW
397
                     !check_matching_dictionary(src_dictionary, dst_dictionary, key_src.get_string(),
×
NEW
398
                                                to_collection_type(src_any))) {
×
NEW
399
                to_insert.push_back(src_ndx);
×
NEW
400
            }
×
NEW
401
            src_ndx += 1;
×
NEW
402
            dst_ndx += 1;
×
NEW
403
        }
×
NEW
404
        else if (cmp < 0) {
×
NEW
405
            to_insert.push_back(src_ndx);
×
NEW
406
            src_ndx += 1;
×
NEW
407
        }
×
NEW
408
        else {
×
NEW
409
            to_delete.push_back(dst_ndx);
×
NEW
410
            dst_ndx += 1;
×
NEW
411
        }
×
NEW
412
    }
×
413

414
    // append src to dst
NEW
415
    while (src_ndx < src_dictionary.size()) {
×
NEW
416
        to_insert.push_back(src_ndx);
×
NEW
417
        src_ndx += 1;
×
NEW
418
    }
×
419

420
    // delete everything that did not match passed src.size()
NEW
421
    while (dst_ndx < dst_dictionary.size()) {
×
NEW
422
        to_delete.push_back(dst_ndx);
×
NEW
423
        dst_ndx += 1;
×
NEW
424
    }
×
425

426
    // delete all the non matching keys
NEW
427
    while (!to_delete.empty()) {
×
NEW
428
        dst_dictionary.erase(dst_dictionary.begin() + to_delete.back());
×
NEW
429
        to_delete.pop_back();
×
NEW
430
    }
×
431

432
    // insert into dst
NEW
433
    for (const auto pos : to_insert) {
×
NEW
434
        const auto [key, any] = src_dictionary.get_pair(pos);
×
NEW
435
        if (is_collection(any)) {
×
NEW
436
            auto coll_type = to_collection_type(any);
×
NEW
437
            dst_dictionary.insert_collection(key.get_string(), coll_type);
×
NEW
438
            copy_dictionary_in_mixed(src_dictionary, dst_dictionary, key.get_string(), coll_type);
×
NEW
439
        }
×
NEW
440
        else {
×
NEW
441
            dst_dictionary.insert(key, any);
×
NEW
442
        }
×
NEW
443
    }
×
NEW
444
}
×
445

446
bool InterRealmValueConverter::check_matching_list(const Lst<Mixed>& src_list, Lst<Mixed>& dst_list, size_t ndx,
447
                                                   CollectionType type) const
NEW
448
{
×
449

NEW
450
    if (type == CollectionType::List) {
×
NEW
451
        auto nested_src_list = src_list.get_list(ndx);
×
NEW
452
        auto nested_dst_list = dst_list.get_list(ndx);
×
NEW
453
        auto size_src = nested_src_list->size();
×
NEW
454
        auto size_dst = nested_dst_list->size();
×
NEW
455
        if (size_src != size_dst)
×
NEW
456
            return false;
×
NEW
457
        for (size_t i = 0; i < size_src; ++i) {
×
NEW
458
            auto src_mixed = nested_src_list->get_any(i);
×
NEW
459
            auto dst_mixed = nested_dst_list->get_any(i);
×
NEW
460
            if (src_mixed != dst_mixed)
×
NEW
461
                return false;
×
NEW
462
        }
×
NEW
463
    }
×
NEW
464
    else if (type == CollectionType::Dictionary) {
×
NEW
465
        auto nested_src_dictionary = src_list.get_dictionary(ndx);
×
NEW
466
        auto nested_dst_dictionary = dst_list.get_dictionary(ndx);
×
NEW
467
        auto size_src = nested_src_dictionary->size();
×
NEW
468
        auto size_dst = nested_dst_dictionary->size();
×
NEW
469
        if (size_src != size_dst)
×
NEW
470
            return false;
×
NEW
471
        for (size_t i = 0; i < size_src; ++i) {
×
NEW
472
            auto [src_key, src_mixed] = nested_src_dictionary->get_pair(i);
×
NEW
473
            auto [dst_key, dst_mixed] = nested_dst_dictionary->get_pair(i);
×
NEW
474
            if (src_key != dst_key)
×
NEW
475
                return false;
×
NEW
476
            if (src_mixed != dst_mixed)
×
NEW
477
                return false;
×
NEW
478
        }
×
NEW
479
    }
×
NEW
480
    return true;
×
NEW
481
}
×
482

483
bool InterRealmValueConverter::check_matching_dictionary(const Dictionary& src_dictionary,
484
                                                         const Dictionary& dst_dictionary, StringData key,
485
                                                         CollectionType type) const
NEW
486
{
×
NEW
487
    if (type == CollectionType::List) {
×
NEW
488
        auto n_src_list = src_dictionary.get_list(key);
×
NEW
489
        auto n_dst_list = dst_dictionary.get_list(key);
×
NEW
490
        auto size_src = n_src_list->size();
×
NEW
491
        auto size_dst = n_dst_list->size();
×
NEW
492
        if (size_src != size_dst)
×
NEW
493
            return false;
×
NEW
494
        for (size_t i = 0; i < size_src; ++i) {
×
NEW
495
            auto src_mixed = n_src_list->get_any(i);
×
NEW
496
            auto dst_mixed = n_dst_list->get_any(i);
×
NEW
497
            if (src_mixed != dst_mixed)
×
NEW
498
                return false;
×
NEW
499
        }
×
NEW
500
    }
×
NEW
501
    else if (type == CollectionType::Dictionary) {
×
NEW
502
        auto n_src_dictionary = src_dictionary.get_dictionary(key);
×
NEW
503
        auto n_dst_dictionary = dst_dictionary.get_dictionary(key);
×
NEW
504
        auto size_src = n_src_dictionary->size();
×
NEW
505
        auto size_dst = n_dst_dictionary->size();
×
NEW
506
        if (size_src != size_dst)
×
NEW
507
            return false;
×
NEW
508
        for (size_t i = 0; i < size_src; ++i) {
×
NEW
509
            auto [src_key, src_mixed] = n_src_dictionary->get_pair(i);
×
NEW
510
            auto [dst_key, dst_mixed] = n_dst_dictionary->get_pair(i);
×
NEW
511
            if (src_key != dst_key)
×
NEW
512
                return false;
×
NEW
513
            if (src_mixed != dst_mixed)
×
NEW
514
                return false;
×
NEW
515
        }
×
NEW
516
    }
×
NEW
517
    return true;
×
NEW
518
}
×
519

520
void InterRealmValueConverter::copy_list_in_mixed(const Lst<Mixed>& src_list, Lst<Mixed>& dst_list, size_t ndx,
521
                                                  CollectionType type) const
NEW
522
{
×
NEW
523
    if (type == CollectionType::List) {
×
NEW
524
        auto n_src_list = src_list.get_list(ndx);
×
NEW
525
        auto n_dst_list = dst_list.get_list(ndx);
×
NEW
526
        handle_list_in_mixed(*n_src_list, *n_dst_list);
×
NEW
527
    }
×
NEW
528
    else if (type == CollectionType::Set) {
×
NEW
529
        auto n_src_set = src_list.get_set(ndx);
×
NEW
530
        auto n_dst_set = dst_list.get_set(ndx);
×
NEW
531
        copy_set(*n_src_set, *n_dst_set, nullptr);
×
NEW
532
    }
×
NEW
533
    else if (type == CollectionType::Dictionary) {
×
NEW
534
        auto n_src_dict = src_list.get_dictionary(ndx);
×
NEW
535
        auto n_dst_dict = dst_list.get_dictionary(ndx);
×
NEW
536
        handle_dictionary_in_mixed(*n_src_dict, *n_dst_dict);
×
NEW
537
    }
×
NEW
538
}
×
539

540
void InterRealmValueConverter::copy_dictionary_in_mixed(const Dictionary& src_dictionary, Dictionary& dst_dictionary,
541
                                                        StringData key, CollectionType type) const
NEW
542
{
×
NEW
543
    if (type == CollectionType::List) {
×
NEW
544
        auto n_src_list = src_dictionary.get_list(key);
×
NEW
545
        auto n_dst_list = dst_dictionary.get_list(key);
×
NEW
546
        handle_list_in_mixed(*n_src_list, *n_dst_list);
×
NEW
547
    }
×
NEW
548
    else if (type == CollectionType::Set) {
×
NEW
549
        auto n_src_set = src_dictionary.get_set(key);
×
NEW
550
        auto n_dst_set = dst_dictionary.get_set(key);
×
NEW
551
        copy_set(*n_src_set, *n_dst_set, nullptr);
×
NEW
552
    }
×
NEW
553
    else if (type == CollectionType::Dictionary) {
×
NEW
554
        auto n_src_dictionary = src_dictionary.get_dictionary(key);
×
NEW
555
        auto n_dst_dictionary = dst_dictionary.get_dictionary(key);
×
NEW
556
        handle_dictionary_in_mixed(*n_src_dictionary, *n_dst_dictionary);
×
NEW
557
    }
×
NEW
558
}
×
559

560
bool InterRealmValueConverter::is_collection(Mixed mixed) const
NEW
561
{
×
NEW
562
    return mixed.is_type(type_List, type_Set, type_Dictionary);
×
NEW
563
}
×
564

565
CollectionType InterRealmValueConverter::to_collection_type(Mixed mixed) const
NEW
566
{
×
NEW
567
    const auto mixed_type = mixed.get_type();
×
NEW
568
    if (mixed_type == type_List)
×
NEW
569
        return CollectionType::List;
×
NEW
570
    if (mixed_type == type_Set)
×
NEW
571
        return CollectionType::Set;
×
NEW
572
    if (mixed_type == type_Dictionary)
×
NEW
573
        return CollectionType::Dictionary;
×
NEW
574
    REALM_UNREACHABLE();
×
NEW
575
}
×
576

577
// If an embedded object is encountered, add it to a list of embedded objects to process.
578
// This relies on the property that embedded objects only have one incoming link
579
// otherwise there could be an infinite loop while discovering embedded objects.
580
void EmbeddedObjectConverter::track(const Obj& e_src, const Obj& e_dst)
581
{
67,158✔
582
    embedded_pending.push_back({e_src, e_dst});
67,158✔
583
}
67,158✔
584

585
void EmbeddedObjectConverter::process_pending()
586
{
29,448✔
587
    util::FlatMap<TableKey, InterRealmObjectConverter> converters;
29,448✔
588

14,724✔
589
    while (!embedded_pending.empty()) {
96,606✔
590
        EmbeddedToCheck pending = embedded_pending.back();
67,158✔
591
        embedded_pending.pop_back();
67,158✔
592

33,579✔
593
        TableRef dst_table = pending.embedded_in_dst.get_table();
67,158✔
594
        TableKey dst_table_key = dst_table->get_key();
67,158✔
595
        auto it = converters.find(dst_table_key);
67,158✔
596
        if (it == converters.end()) {
67,158✔
597
            TableRef src_table = pending.embedded_in_src.get_table();
1,074✔
598
            it = converters.insert({dst_table_key, InterRealmObjectConverter{src_table, dst_table, this}}).first;
1,074✔
599
        }
1,074✔
600
        InterRealmObjectConverter& converter = it->second;
67,158✔
601
        converter.copy(pending.embedded_in_src, pending.embedded_in_dst, nullptr);
67,158✔
602
    }
67,158✔
603
}
29,448✔
604

605
InterRealmValueConverter::InterRealmValueConverter(ConstTableRef src_table, ColKey src_col, ConstTableRef dst_table,
606
                                                   ColKey dst_col, EmbeddedObjectConverter* ec)
607
    : m_src_table(src_table)
608
    , m_dst_table(dst_table)
609
    , m_src_col(src_col)
610
    , m_dst_col(dst_col)
611
    , m_embedded_converter(ec)
612
    , m_is_embedded_link(false)
613
    , m_primitive_types_only(!(src_col.get_type() == col_type_TypedLink || src_col.get_type() == col_type_Link ||
614
                               src_col.get_type() == col_type_LinkList || src_col.get_type() == col_type_Mixed))
615
{
57,078✔
616
    if (!m_primitive_types_only) {
57,078✔
617
        REALM_ASSERT(src_table);
8,784✔
618
        m_opposite_of_src = src_table->get_opposite_table(src_col);
8,784✔
619
        m_opposite_of_dst = dst_table->get_opposite_table(dst_col);
8,784✔
620
        REALM_ASSERT(bool(m_opposite_of_src) == bool(m_opposite_of_dst));
8,784✔
621
        if (m_opposite_of_src) {
8,784✔
622
            m_is_embedded_link = m_opposite_of_src->is_embedded();
4,968✔
623
        }
4,968✔
624
    }
8,784✔
625
}
57,078✔
626

627
void InterRealmValueConverter::track_new_embedded(const Obj& src, const Obj& dst) const
628
{
6,840✔
629
    m_embedded_converter->track(src, dst);
6,840✔
630
}
6,840✔
631

632
// convert `src` to the destination Realm and compare that value with `dst`
633
// If `converted_src_out` is provided, it will be set to the converted src value
634
int InterRealmValueConverter::cmp_src_to_dst(Mixed src, Mixed dst, ConversionResult* converted_src_out,
635
                                             bool* did_update_out) const
636
{
163,284✔
637
    int cmp = 0;
163,284✔
638
    Mixed converted_src;
163,284✔
639
    if (m_primitive_types_only || !src.is_type(type_Link, type_TypedLink)) {
163,284✔
640
        converted_src = src;
149,856✔
641
        cmp = src.compare(dst);
149,856✔
642
    }
149,856✔
643
    else if (m_opposite_of_src) {
13,428✔
644
        ObjKey src_link_key = src.get<ObjKey>();
10,320✔
645
        if (m_is_embedded_link) {
10,320✔
646
            Obj src_embedded = m_opposite_of_src->get_object(src_link_key);
6,858✔
647
            REALM_ASSERT_DEBUG(src_embedded.is_valid());
6,858✔
648
            if (dst.is_type(type_Link, type_TypedLink)) {
6,858✔
649
                cmp = 0; // no need to set this link, there is already an embedded object here
5,790✔
650
                Obj dst_embedded = m_opposite_of_dst->get_object(dst.get<ObjKey>());
5,790✔
651
                REALM_ASSERT_DEBUG(dst_embedded.is_valid());
5,790✔
652
                converted_src = dst_embedded.get_key();
5,790✔
653
                track_new_embedded(src_embedded, dst_embedded);
5,790✔
654
            }
5,790✔
655
            else {
1,068✔
656
                cmp = src.compare(dst);
1,068✔
657
                if (converted_src_out) {
1,068✔
658
                    converted_src_out->requires_new_embedded_object = true;
1,050✔
659
                    converted_src_out->src_embedded_to_check = src_embedded;
1,050✔
660
                }
1,050✔
661
            }
1,068✔
662
        }
6,858✔
663
        else {
3,462✔
664
            Obj dst_link;
3,462✔
665
            if (m_opposite_of_dst == m_opposite_of_src) {
3,462✔
666
                // if this is the same Realm, we can use the ObjKey
96✔
667
                dst_link = m_opposite_of_dst->get_object(src_link_key);
192✔
668
            }
192✔
669
            else {
3,270✔
670
                // in different Realms we create a new object
1,635✔
671
                if (m_opposite_of_src->get_primary_key_column()) {
3,270✔
672
                    Mixed src_link_pk = m_opposite_of_src->get_primary_key(src_link_key);
3,270✔
673
                    dst_link = m_opposite_of_dst->create_object_with_primary_key(src_link_pk, did_update_out);
3,270✔
674
                }
3,270✔
675
                else {
×
676
                    dst_link = m_opposite_of_dst->create_object();
×
677
                }
×
678
            }
3,270✔
679
            converted_src = dst_link.get_key();
3,462✔
680
            if (dst.is_type(type_TypedLink)) {
3,462✔
681
                cmp = converted_src.compare(dst.get<ObjKey>());
2,724✔
682
            }
2,724✔
683
            else {
738✔
684
                cmp = converted_src.compare(dst);
738✔
685
            }
738✔
686
        }
3,462✔
687
    }
10,320✔
688
    else {
3,108✔
689
        ObjLink src_link = src.get<ObjLink>();
3,108✔
690
        if (src_link.is_unresolved()) {
3,108✔
691
            converted_src = Mixed{}; // no need to transfer over unresolved links
×
692
            cmp = converted_src.compare(dst);
×
693
        }
×
694
        else {
3,108✔
695
            TableRef src_link_table = m_src_table->get_parent_group()->get_table(src_link.get_table_key());
3,108✔
696
            REALM_ASSERT_EX(src_link_table, src_link.get_table_key());
3,108✔
697
            TableRef dst_link_table = m_dst_table->get_parent_group()->get_table(src_link_table->get_name());
3,108✔
698
            REALM_ASSERT_EX(dst_link_table, src_link_table->get_name());
3,108✔
699
            // embedded tables should always be covered by the m_opposite_of_src case above.
1,554✔
700
            REALM_ASSERT_EX(!src_link_table->is_embedded(), src_link_table->get_name());
3,108✔
701
            // regular table, convert by pk
1,554✔
702
            if (src_link_table->get_primary_key_column()) {
3,108✔
703
                Mixed src_pk = src_link_table->get_primary_key(src_link.get_obj_key());
3,108✔
704
                Obj dst_link = dst_link_table->create_object_with_primary_key(src_pk, did_update_out);
3,108✔
705
                converted_src = ObjLink{dst_link_table->get_key(), dst_link.get_key()};
3,108✔
706
            }
3,108✔
707
            else if (src_link_table == dst_link_table) {
×
708
                // no pk, but this is the same Realm, so convert by ObjKey
709
                Obj dst_link = dst_link_table->get_object(src_link.get_obj_key());
×
710
                converted_src = ObjLink{dst_link_table->get_key(), dst_link.get_key()};
×
711
            }
×
712
            else {
×
713
                // no pk, and different Realm, create an object
714
                Obj dst_link = dst_link_table->create_object();
×
715
                converted_src = ObjLink{dst_link_table->get_key(), dst_link.get_key()};
×
716
            }
×
717
            cmp = converted_src.compare(dst);
3,108✔
718
        }
3,108✔
719
    }
3,108✔
720
    if (converted_src_out) {
163,284✔
721
        converted_src_out->converted_value = converted_src;
127,224✔
722
    }
127,224✔
723
    if (did_update_out && cmp) {
163,284✔
724
        *did_update_out = true;
14,976✔
725
    }
14,976✔
726
    return cmp;
163,284✔
727
}
163,284✔
728

729
InterRealmObjectConverter::InterRealmObjectConverter(ConstTableRef table_src, TableRef table_dst,
730
                                                     EmbeddedObjectConverter* embedded_tracker)
731
    : m_embedded_tracker(embedded_tracker)
732
{
23,910✔
733
    m_columns_cache.reserve(table_src->get_column_count());
23,910✔
734
    ColKey pk_col = table_src->get_primary_key_column();
23,910✔
735
    for (ColKey col_key_src : table_src->get_column_keys()) {
78,630✔
736
        if (col_key_src == pk_col)
78,630✔
737
            continue;
22,836✔
738
        StringData col_name = table_src->get_column_name(col_key_src);
55,794✔
739
        ColKey col_key_dst = table_dst->get_column_key(col_name);
55,794✔
740
        REALM_ASSERT(col_key_dst);
55,794✔
741
        m_columns_cache.emplace_back(table_src, col_key_src, table_dst, col_key_dst, m_embedded_tracker);
55,794✔
742
    }
55,794✔
743
}
23,910✔
744

745
void InterRealmObjectConverter::copy(const Obj& src, Obj& dst, bool* update_out)
746
{
99,270✔
747
    for (auto& column : m_columns_cache) {
151,212✔
748
        column.copy_value(src, dst, update_out);
151,212✔
749
    }
151,212✔
750
}
99,270✔
751

752
} // 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

© 2025 Coveralls, Inc