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

realm / realm-core / 1850

20 Nov 2023 10:36AM UTC coverage: 91.692% (-0.2%) from 91.907%
1850

push

Evergreen

web-flow
Stop failing when sync is disabled and MacOs debug tests are set to run (#7146)

92316 of 169214 branches covered (0.0%)

231566 of 252547 relevant lines covered (91.69%)

6538009.25 hits per line

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

91.1
/src/realm/list.cpp
1
/*************************************************************************
2
 *
3
 * Copyright 2016 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

20
#include "realm/list.hpp"
21
#include "realm/cluster_tree.hpp"
22
#include "realm/array_basic.hpp"
23
#include "realm/array_integer.hpp"
24
#include "realm/array_bool.hpp"
25
#include "realm/array_string.hpp"
26
#include "realm/array_binary.hpp"
27
#include "realm/array_timestamp.hpp"
28
#include "realm/array_decimal128.hpp"
29
#include "realm/array_fixed_bytes.hpp"
30
#include "realm/array_typed_link.hpp"
31
#include "realm/array_mixed.hpp"
32
#include "realm/column_type_traits.hpp"
33
#include "realm/object_id.hpp"
34
#include "realm/table.hpp"
35
#include "realm/table_view.hpp"
36
#include "realm/group.hpp"
37
#include "realm/replication.hpp"
38
#include "realm/index_string.hpp"
39

40
namespace realm {
41

42
// FIXME: This method belongs in obj.cpp.
43
LstBasePtr Obj::get_listbase_ptr(ColKey col_key) const
44
{
345,000✔
45
    auto attr = get_table()->get_column_attr(col_key);
345,000✔
46
    REALM_ASSERT(attr.test(col_attr_List));
345,000✔
47
    bool nullable = attr.test(col_attr_Nullable);
345,000✔
48

173,472✔
49
    switch (get_table()->get_column_type(col_key)) {
345,000✔
50
        case type_Int: {
47,052✔
51
            if (nullable)
47,052✔
52
                return std::make_unique<Lst<util::Optional<Int>>>(*this, col_key);
4,965✔
53
            else
42,087✔
54
                return std::make_unique<Lst<Int>>(*this, col_key);
42,087✔
55
        }
×
56
        case type_Bool: {
3,360✔
57
            if (nullable)
3,360✔
58
                return std::make_unique<Lst<util::Optional<Bool>>>(*this, col_key);
1,704✔
59
            else
1,656✔
60
                return std::make_unique<Lst<Bool>>(*this, col_key);
1,656✔
61
        }
×
62
        case type_Float: {
3,384✔
63
            if (nullable)
3,384✔
64
                return std::make_unique<Lst<util::Optional<Float>>>(*this, col_key);
1,734✔
65
            else
1,650✔
66
                return std::make_unique<Lst<Float>>(*this, col_key);
1,650✔
67
        }
×
68
        case type_Double: {
3,678✔
69
            if (nullable)
3,678✔
70
                return std::make_unique<Lst<util::Optional<Double>>>(*this, col_key);
1,734✔
71
            else
1,944✔
72
                return std::make_unique<Lst<Double>>(*this, col_key);
1,944✔
73
        }
×
74
        case type_String: {
231,780✔
75
            return std::make_unique<Lst<String>>(*this, col_key);
231,780✔
76
        }
×
77
        case type_Binary: {
3,354✔
78
            return std::make_unique<Lst<Binary>>(*this, col_key);
3,354✔
79
        }
×
80
        case type_Timestamp: {
3,378✔
81
            return std::make_unique<Lst<Timestamp>>(*this, col_key);
3,378✔
82
        }
×
83
        case type_Decimal: {
3,366✔
84
            return std::make_unique<Lst<Decimal128>>(*this, col_key);
3,366✔
85
        }
×
86
        case type_ObjectId: {
3,426✔
87
            if (nullable)
3,426✔
88
                return std::make_unique<Lst<util::Optional<ObjectId>>>(*this, col_key);
1,752✔
89
            else
1,674✔
90
                return std::make_unique<Lst<ObjectId>>(*this, col_key);
1,674✔
91
        }
×
92
        case type_UUID: {
3,402✔
93
            if (nullable)
3,402✔
94
                return std::make_unique<Lst<util::Optional<UUID>>>(*this, col_key);
1,740✔
95
            else
1,662✔
96
                return std::make_unique<Lst<UUID>>(*this, col_key);
1,662✔
97
        }
×
98
        case type_TypedLink: {
✔
99
            return std::make_unique<Lst<ObjLink>>(*this, col_key);
×
100
        }
×
101
        case type_Mixed: {
4,572✔
102
            return std::make_unique<Lst<Mixed>>(*this, col_key);
4,572✔
103
        }
×
104
        case type_LinkList:
34,248✔
105
            return get_linklist_ptr(col_key);
34,248✔
106
        case type_Link:
✔
107
            break;
×
108
    }
×
109
    REALM_TERMINATE("Unsupported column type");
110
}
×
111

112
/****************************** Lst aggregates *******************************/
113

114
namespace {
115
void do_sort(std::vector<size_t>& indices, size_t size, util::FunctionRef<bool(size_t, size_t)> comp)
116
{
5,574✔
117
    auto old_size = indices.size();
5,574✔
118
    indices.reserve(size);
5,574✔
119
    if (size < old_size) {
5,574✔
120
        // If list size has decreased, we have to start all over
54✔
121
        indices.clear();
108✔
122
        old_size = 0;
108✔
123
    }
108✔
124
    for (size_t i = old_size; i < size; i++) {
34,428✔
125
        // If list size has increased, just add the missing indices
14,427✔
126
        indices.push_back(i);
28,854✔
127
    }
28,854✔
128

2,787✔
129
    auto b = indices.begin();
5,574✔
130
    auto e = indices.end();
5,574✔
131
    std::sort(b, e, comp);
5,574✔
132
}
5,574✔
133
} // anonymous namespace
134

135
template <class T>
136
void Lst<T>::sort(std::vector<size_t>& indices, bool ascending) const
137
{
5,574✔
138
    update_if_needed();
5,574✔
139

2,787✔
140
    auto tree = m_tree.get();
5,574✔
141
    if (ascending) {
5,574!
142
        do_sort(indices, size(), [tree](size_t i1, size_t i2) {
71,001✔
143
            return unresolved_to_null(tree->get(i1)) < unresolved_to_null(tree->get(i2));
71,001✔
144
        });
71,001✔
145
    }
4,710✔
146
    else {
864✔
147
        do_sort(indices, size(), [tree](size_t i1, size_t i2) {
18,861✔
148
            return unresolved_to_null(tree->get(i1)) > unresolved_to_null(tree->get(i2));
18,861✔
149
        });
18,861✔
150
    }
864✔
151
}
5,574✔
152

153
// std::unique, but leaving the minimum value rather than the first found value
154
// for runs of duplicates. This makes distinct stable without relying on a
155
// stable sort, which makes it easier to write tests and avoids surprising results
156
// where distinct appears to change the order of elements
157
template <class Iterator, class Predicate>
158
static Iterator min_unique(Iterator first, Iterator last, Predicate pred)
159
{
858✔
160
    if (first == last) {
858!
161
        return first;
×
162
    }
×
163

429✔
164
    Iterator result = first;
858✔
165
    while (++first != last) {
6,792!
166
        bool equal = pred(*result, *first);
5,934✔
167
        if ((equal && *result > *first) || (!equal && ++result != first))
5,934!
168
            *result = *first;
2,766✔
169
    }
5,934✔
170
    return ++result;
858✔
171
}
858✔
172

173
template <class T>
174
void Lst<T>::distinct(std::vector<size_t>& indices, util::Optional<bool> sort_order) const
175
{
858✔
176
    indices.clear();
858✔
177
    sort(indices, sort_order.value_or(true));
858✔
178
    if (indices.empty()) {
858!
179
        return;
×
180
    }
×
181

429✔
182
    auto tree = m_tree.get();
858✔
183
    auto duplicates = min_unique(indices.begin(), indices.end(), [tree](size_t i1, size_t i2) noexcept {
5,934✔
184
        return unresolved_to_null(tree->get(i1)) == unresolved_to_null(tree->get(i2));
5,934✔
185
    });
5,934✔
186

429✔
187
    // Erase the duplicates
429✔
188
    indices.erase(duplicates, indices.end());
858✔
189

429✔
190
    if (!sort_order) {
858!
191
        // Restore original order
309✔
192
        std::sort(indices.begin(), indices.end());
618✔
193
    }
618✔
194
}
858✔
195

196
/***************************** Lst<Stringdata> ******************************/
197

198
template <>
199
void Lst<StringData>::do_insert(size_t ndx, StringData value)
200
{
636,576✔
201
    if (auto index = m_obj.get_table()->get_search_index(m_col_key)) {
636,576✔
202
        // Inserting a value already present is idempotent
84✔
203
        index->insert(m_obj.get_key(), value);
168✔
204
    }
168✔
205
    m_tree->insert(ndx, value);
636,576✔
206
}
636,576✔
207

208
template <>
209
void Lst<StringData>::do_set(size_t ndx, StringData value)
210
{
6,225✔
211
    if (auto index = m_obj.get_table()->get_search_index(m_col_key)) {
6,225✔
212
        auto old_value = m_tree->get(ndx);
30✔
213
        size_t nb_old = 0;
30✔
214
        m_tree->for_all([&](StringData val) {
120✔
215
            if (val == old_value) {
120✔
216
                nb_old++;
42✔
217
            }
42✔
218
            return !(nb_old > 1);
120✔
219
        });
120✔
220

15✔
221
        if (nb_old == 1) {
30✔
222
            // Remove last one
9✔
223
            index->erase_string(m_obj.get_key(), old_value);
18✔
224
        }
18✔
225
        // Inserting a value already present is idempotent
15✔
226
        index->insert(m_obj.get_key(), value);
30✔
227
    }
30✔
228
    m_tree->set(ndx, value);
6,225✔
229
}
6,225✔
230

231
template <>
232
inline void Lst<StringData>::do_remove(size_t ndx)
233
{
18,684✔
234
    if (auto index = m_obj.get_table()->get_search_index(m_col_key)) {
18,684✔
235
        auto old_value = m_tree->get(ndx);
6✔
236
        size_t nb_old = 0;
6✔
237
        m_tree->for_all([&](StringData val) {
30✔
238
            if (val == old_value) {
30✔
239
                nb_old++;
6✔
240
            }
6✔
241
            return !(nb_old > 1);
30✔
242
        });
30✔
243

3✔
244
        if (nb_old == 1) {
6✔
245
            index->erase_string(m_obj.get_key(), old_value);
6✔
246
        }
6✔
247
    }
6✔
248
    m_tree->erase(ndx);
18,684✔
249
}
18,684✔
250

251
template <>
252
inline void Lst<StringData>::do_clear()
253
{
225✔
254
    if (auto index = m_obj.get_table()->get_search_index(m_col_key)) {
225✔
255
        index->erase_list(m_obj.get_key(), *this);
6✔
256
    }
6✔
257
    m_tree->clear();
225✔
258
}
225✔
259

260
/********************************* Lst<Key> *********************************/
261

262
template <>
263
void Lst<ObjKey>::do_set(size_t ndx, ObjKey target_key)
264
{
50,724✔
265
    auto origin_table = m_obj.get_table();
50,724✔
266
    auto target_table_key = origin_table->get_opposite_table_key(m_col_key);
50,724✔
267
    ObjKey old_key = this->get(ndx);
50,724✔
268
    CascadeState state(CascadeState::Mode::Strong);
50,724✔
269
    bool recurse =
50,724✔
270
        m_obj.replace_backlink(m_col_key, {target_table_key, old_key}, {target_table_key, target_key}, state);
50,724✔
271

25,362✔
272
    m_tree->set(ndx, target_key);
50,724✔
273

25,362✔
274
    if (recurse) {
50,724✔
275
        _impl::TableFriend::remove_recursive(*origin_table, state); // Throws
60✔
276
    }
60✔
277
    if (target_key.is_unresolved()) {
50,724✔
278
        if (!old_key.is_unresolved())
318✔
279
            m_tree->set_context_flag(true);
318✔
280
    }
318✔
281
    else if (old_key.is_unresolved()) {
50,406✔
282
        // We might have removed the last unresolved link - check it
4,530✔
283
        _impl::check_for_last_unresolved(m_tree.get());
9,060✔
284
    }
9,060✔
285
}
50,724✔
286

287
template <>
288
void Lst<ObjKey>::do_insert(size_t ndx, ObjKey target_key)
289
{
6,407,814✔
290
    auto origin_table = m_obj.get_table();
6,407,814✔
291
    auto target_table_key = origin_table->get_opposite_table_key(m_col_key);
6,407,814✔
292
    m_obj.set_backlink(m_col_key, {target_table_key, target_key});
6,407,814✔
293
    m_tree->insert(ndx, target_key);
6,407,814✔
294
    if (target_key.is_unresolved()) {
6,407,814✔
295
        m_tree->set_context_flag(true);
9,084✔
296
    }
9,084✔
297
}
6,407,814✔
298

299
template <>
300
void Lst<ObjKey>::do_remove(size_t ndx)
301
{
2,394✔
302
    auto origin_table = m_obj.get_table();
2,394✔
303
    auto target_table_key = origin_table->get_opposite_table_key(m_col_key);
2,394✔
304
    ObjKey old_key = get(ndx);
2,394✔
305
    CascadeState state(old_key.is_unresolved() ? CascadeState::Mode::All : CascadeState::Mode::Strong);
2,385✔
306

1,197✔
307
    bool recurse = m_obj.remove_backlink(m_col_key, {target_table_key, old_key}, state);
2,394✔
308

1,197✔
309
    m_tree->erase(ndx);
2,394✔
310

1,197✔
311
    if (recurse) {
2,394✔
312
        _impl::TableFriend::remove_recursive(*origin_table, state); // Throws
918✔
313
    }
918✔
314
    if (old_key.is_unresolved()) {
2,394✔
315
        // We might have removed the last unresolved link - check it
9✔
316
        _impl::check_for_last_unresolved(m_tree.get());
18✔
317
    }
18✔
318
}
2,394✔
319

320
template <>
321
void Lst<ObjKey>::do_clear()
322
{
2,742✔
323
    auto origin_table = m_obj.get_table();
2,742✔
324
    TableRef target_table = m_obj.get_target_table(m_col_key);
2,742✔
325

1,371✔
326
    size_t sz = size();
2,742✔
327
    if (!target_table->is_embedded()) {
2,742✔
328
        size_t ndx = sz;
396✔
329
        while (ndx--) {
41,016✔
330
            do_set(ndx, null_key);
40,620✔
331
            m_tree->erase(ndx);
40,620✔
332
        }
40,620✔
333
        m_tree->set_context_flag(false);
396✔
334
        return;
396✔
335
    }
396✔
336

1,173✔
337
    TableKey target_table_key = target_table->get_key();
2,346✔
338
    ColKey backlink_col = origin_table->get_opposite_column(m_col_key);
2,346✔
339

1,173✔
340
    CascadeState state;
2,346✔
341

1,173✔
342
    typedef _impl::TableFriend tf;
2,346✔
343
    for (size_t ndx = 0; ndx < sz; ++ndx) {
4,818✔
344
        ObjKey target_key = m_tree->get(ndx);
2,472✔
345
        Obj target_obj = target_table->get_object(target_key);
2,472✔
346
        target_obj.remove_one_backlink(backlink_col, m_obj.get_key()); // Throws
2,472✔
347
        // embedded objects should only have one incoming link
1,236✔
348
        REALM_ASSERT_EX(target_obj.get_backlink_count() == 0, target_obj.get_backlink_count());
2,472✔
349
        state.m_to_be_deleted.emplace_back(target_table_key, target_key);
2,472✔
350
    }
2,472✔
351

1,173✔
352
    m_tree->clear();
2,346✔
353
    m_tree->set_context_flag(false);
2,346✔
354

1,173✔
355
    tf::remove_recursive(*origin_table, state); // Throws
2,346✔
356
}
2,346✔
357

358
template <>
359
void Lst<ObjLink>::do_set(size_t ndx, ObjLink target_link)
360
{
6✔
361
    ObjLink old_link = get(ndx);
6✔
362
    CascadeState state(old_link.get_obj_key().is_unresolved() ? CascadeState::Mode::All : CascadeState::Mode::Strong);
6✔
363
    bool recurse = m_obj.replace_backlink(m_col_key, old_link, target_link, state);
6✔
364

3✔
365
    m_tree->set(ndx, target_link);
6✔
366

3✔
367
    if (recurse) {
6✔
368
        auto origin_table = m_obj.get_table();
×
369
        _impl::TableFriend::remove_recursive(*origin_table, state); // Throws
×
370
    }
×
371
}
6✔
372

373
template <>
374
void Lst<ObjLink>::do_insert(size_t ndx, ObjLink target_link)
375
{
24✔
376
    m_obj.set_backlink(m_col_key, target_link);
24✔
377
    m_tree->insert(ndx, target_link);
24✔
378
}
24✔
379

380
template <>
381
void Lst<ObjLink>::do_remove(size_t ndx)
382
{
6✔
383
    ObjLink old_link = get(ndx);
6✔
384
    CascadeState state(old_link.get_obj_key().is_unresolved() ? CascadeState::Mode::All : CascadeState::Mode::Strong);
6✔
385

3✔
386
    bool recurse = m_obj.remove_backlink(m_col_key, old_link, state);
6✔
387

3✔
388
    m_tree->erase(ndx);
6✔
389

3✔
390
    if (recurse) {
6✔
391
        auto table = m_obj.get_table();
×
392
        _impl::TableFriend::remove_recursive(*table, state); // Throws
×
393
    }
×
394
}
6✔
395

396
template <>
397
void Lst<Mixed>::do_set(size_t ndx, Mixed value)
398
{
654✔
399
    ObjLink old_link;
654✔
400
    ObjLink target_link;
654✔
401
    Mixed old_value = m_tree->get(ndx);
654✔
402

327✔
403
    if (old_value.is_type(type_TypedLink)) {
654✔
404
        old_link = old_value.get<ObjLink>();
198✔
405
    }
198✔
406
    if (value.is_type(type_TypedLink)) {
654✔
407
        target_link = value.get<ObjLink>();
192✔
408
        m_obj.get_table()->get_parent_group()->validate(target_link);
192✔
409
    }
192✔
410

327✔
411
    CascadeState state(old_link.get_obj_key().is_unresolved() ? CascadeState::Mode::All : CascadeState::Mode::Strong);
654✔
412
    bool recurse = m_obj.replace_backlink(m_col_key, old_link, target_link, state);
654✔
413

327✔
414
    m_tree->set(ndx, value);
654✔
415

327✔
416
    if (recurse) {
654✔
417
        auto origin_table = m_obj.get_table();
×
418
        _impl::TableFriend::remove_recursive(*origin_table, state); // Throws
×
419
    }
×
420
}
654✔
421

422
template <>
423
void Lst<Mixed>::do_insert(size_t ndx, Mixed value)
424
{
11,484✔
425
    if (value.is_type(type_TypedLink)) {
11,484✔
426
        m_obj.set_backlink(m_col_key, value.get<ObjLink>());
5,208✔
427
    }
5,208✔
428
    m_tree->insert(ndx, value);
11,484✔
429
}
11,484✔
430

431
template <>
432
void Lst<Mixed>::do_remove(size_t ndx)
433
{
2,850✔
434
    if (Mixed old_value = m_tree->get(ndx); old_value.is_type(type_TypedLink)) {
2,850✔
435
        auto old_link = old_value.get<ObjLink>();
1,224✔
436

612✔
437
        CascadeState state(old_link.get_obj_key().is_unresolved() ? CascadeState::Mode::All
615✔
438
                                                                  : CascadeState::Mode::Strong);
1,221✔
439
        bool recurse = m_obj.remove_backlink(m_col_key, old_link, state);
1,224✔
440

612✔
441
        m_tree->erase(ndx);
1,224✔
442

612✔
443
        if (recurse) {
1,224✔
444
            auto table = m_obj.get_table();
×
445
            _impl::TableFriend::remove_recursive(*table, state); // Throws
×
446
        }
×
447
    }
1,224✔
448
    else {
1,626✔
449
        m_tree->erase(ndx);
1,626✔
450
    }
1,626✔
451
}
2,850✔
452

453
template <>
454
void Lst<Mixed>::do_clear()
455
{
234✔
456
    size_t ndx = size();
234✔
457
    while (ndx--) {
1,992✔
458
        do_remove(ndx);
1,758✔
459
    }
1,758✔
460
}
234✔
461

462
Obj LnkLst::create_and_insert_linked_object(size_t ndx)
463
{
22,863✔
464
    Table& t = *get_target_table();
22,863✔
465
    auto o = t.is_embedded() ? t.create_linked_object() : t.create_object();
22,863✔
466
    m_list.insert(ndx, o.get_key());
22,863✔
467
    return o;
22,863✔
468
}
22,863✔
469

470
Obj LnkLst::create_and_set_linked_object(size_t ndx)
471
{
78✔
472
    Table& t = *get_target_table();
78✔
473
    auto o = t.is_embedded() ? t.create_linked_object() : t.create_object();
78✔
474
    m_list.set(ndx, o.get_key());
78✔
475
    return o;
78✔
476
}
78✔
477

478
TableView LnkLst::get_sorted_view(SortDescriptor order) const
479
{
90✔
480
    TableView tv(clone_linklist());
90✔
481
    tv.do_sync();
90✔
482
    tv.sort(std::move(order));
90✔
483
    return tv;
90✔
484
}
90✔
485

486
TableView LnkLst::get_sorted_view(ColKey column_key, bool ascending) const
487
{
72✔
488
    TableView v = get_sorted_view(SortDescriptor({{column_key}}, {ascending}));
72✔
489
    return v;
72✔
490
}
72✔
491

492
void LnkLst::remove_target_row(size_t link_ndx)
493
{
30✔
494
    // Deleting the object will automatically remove all links
15✔
495
    // to it. So we do not have to manually remove the deleted link
15✔
496
    ObjKey k = get(link_ndx);
30✔
497
    get_target_table()->remove_object(k);
30✔
498
}
30✔
499

500
void LnkLst::remove_all_target_rows()
501
{
48✔
502
    if (is_attached()) {
48✔
503
        update_if_needed();
48✔
504
        _impl::TableFriend::batch_erase_rows(*get_target_table(), *m_list.m_tree);
48✔
505
    }
48✔
506
}
48✔
507

508
// Force instantiation:
509
template class Lst<ObjKey>;
510
template class Lst<Mixed>;
511
template class Lst<ObjLink>;
512
template class Lst<int64_t>;
513
template class Lst<bool>;
514
template class Lst<StringData>;
515
template class Lst<BinaryData>;
516
template class Lst<Timestamp>;
517
template class Lst<float>;
518
template class Lst<double>;
519
template class Lst<Decimal128>;
520
template class Lst<ObjectId>;
521
template class Lst<UUID>;
522
template class Lst<util::Optional<int64_t>>;
523
template class Lst<util::Optional<bool>>;
524
template class Lst<util::Optional<float>>;
525
template class Lst<util::Optional<double>>;
526
template class Lst<util::Optional<ObjectId>>;
527
template class Lst<util::Optional<UUID>>;
528

529
} // namespace realm
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