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

realm / realm-core / 1786

28 Oct 2023 12:35PM UTC coverage: 91.562% (-0.02%) from 91.582%
1786

push

Evergreen

web-flow
Improve configurations for sanitized builds (#6911)

* Refactor sanitizer flags for different build types:

** Enable address sanitizer for msvc
** Allow to build with sanitizer for diffent optimized build (also Debug)
** Make RelASAN, RelTSAN, RelUSAN, RelUSAN just shortcuts for half-optimized builds

* Fix usage of moved object for fuzz tester
* Check asan/tsan on macos x64/arm64
* Check asan with msvc 2019
* Remove Jenkins sanitized builders replaced by evergreen configs
* Work-around stack-use-after-scope with msvc2019 and mpark
* Fix crash on check with staled ColKeys
* fix a buffer overrun in a test
* fix a race in async_open_realm test util
* Add some logger related test fixes
* Work around catch2 limmitation with not thread safe asserts and TSAN races
* Run multiprocesses tests under sanitizers
* add assert for an error reported by undefined sanitizer
* Workaround uv scheduler main thread only constraint for callbacks called from non main thread and requesting a realm

---------

Co-authored-by: James Stone <james.stone@mongodb.com>

94310 of 173648 branches covered (0.0%)

54 of 63 new or added lines in 15 files covered. (85.71%)

2212 existing lines in 52 files now uncovered.

230602 of 251853 relevant lines covered (91.56%)

6943670.77 hits per line

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

80.25
/src/realm/cluster.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
#include "realm/cluster.hpp"
20
#include "realm/array_integer.hpp"
21
#include "realm/array_basic.hpp"
22
#include "realm/array_bool.hpp"
23
#include "realm/array_string.hpp"
24
#include "realm/array_binary.hpp"
25
#include "realm/array_mixed.hpp"
26
#include "realm/array_timestamp.hpp"
27
#include "realm/array_decimal128.hpp"
28
#include "realm/array_fixed_bytes.hpp"
29
#include "realm/array_key.hpp"
30
#include "realm/array_ref.hpp"
31
#include "realm/array_typed_link.hpp"
32
#include "realm/array_backlink.hpp"
33
#include "realm/column_type_traits.hpp"
34
#include "realm/replication.hpp"
35
#include "realm/dictionary.hpp"
36
#include <iostream>
37
#include <cmath>
38

39
namespace realm {
40

41
/******************************* FieldValues *********************************/
42

43
FieldValues::FieldValues(std::initializer_list<FieldValue> init)
44
    : m_values(init)
45
{
597,027✔
46
    if (m_values.size() > 1) {
597,027✔
47
        // Sort according to ColKey index
8,124✔
48
        std::sort(m_values.begin(), m_values.end(), [](const auto& a, const auto& b) {
48,639✔
49
            return a.col_key.get_index().val < b.col_key.get_index().val;
48,639✔
50
        });
48,639✔
51
    }
16,248✔
52
}
597,027✔
53

54
void FieldValues::insert(ColKey k, Mixed val, bool is_default)
55
{
534,501✔
56
    if (m_values.empty()) {
534,501✔
57
        m_values.emplace_back(k, val, is_default);
495,864✔
58
        return;
495,864✔
59
    }
495,864✔
60
    unsigned int idx = k.get_index().val;
38,637✔
61
    auto it = std::lower_bound(m_values.begin(), m_values.end(), idx, [](const auto& a, unsigned int i) {
85,506✔
62
        return a.col_key.get_index().val < i;
85,506✔
63
    });
85,506✔
64
    m_values.insert(it, {k, val, is_default});
38,637✔
65
}
38,637✔
66

67
/******************************* ClusterNode *********************************/
68

69
void ClusterNode::IteratorState::clear()
70
{
2,228,967✔
71
    m_current_leaf.detach();
2,228,967✔
72
    m_key_offset = 0;
2,228,967✔
73
    m_current_index = size_t(-1);
2,228,967✔
74
}
2,228,967✔
75

76
void ClusterNode::IteratorState::init(State& s, ObjKey key)
77
{
86,172✔
78
    m_current_leaf.init(s.mem);
86,172✔
79
    m_current_index = s.index;
86,172✔
80
    m_key_offset = key.value - m_current_leaf.get_key_value(m_current_index);
86,172✔
81
    m_current_leaf.set_offset(m_key_offset);
86,172✔
82
}
86,172✔
83

84
const Table* ClusterNode::get_owning_table() const noexcept
85
{
21,474✔
86
    return m_tree_top.get_owning_table();
21,474✔
87
}
21,474✔
88

89
void ClusterNode::get(ObjKey k, ClusterNode::State& state) const
90
{
136,437,408✔
91
    if (!k || !try_get(k, state)) {
136,445,625✔
92
        throw KeyNotFound(util::format("No object with key '%1' in '%2'", k.value, get_owning_table()->get_name()));
462✔
93
    }
462✔
94
}
136,437,408✔
95

96

97
/********************************* Cluster ***********************************/
98

99
MemRef Cluster::create_empty_cluster(Allocator& alloc)
100
{
349,269✔
101
    Array arr(alloc);
349,269✔
102
    arr.create(Array::type_HasRefs); // Throws
349,269✔
103

173,421✔
104
    arr.add(RefOrTagged::make_tagged(0)); // Compact form
349,269✔
105
    return arr.get_mem();
349,269✔
106
}
349,269✔
107

108

109
template <class T>
110
inline void Cluster::do_create(ColKey col)
111
{
170,643✔
112
    T arr(m_alloc);
170,643✔
113
    arr.create();
170,643✔
114
    auto col_ndx = col.get_index();
170,643✔
115
    arr.set_parent(this, col_ndx.val + s_first_col_index);
170,643✔
116
    arr.update_parent();
170,643✔
117
}
170,643✔
118

119
void Cluster::create()
120
{
92,700✔
121
    Array::create(type_HasRefs, false, s_first_col_index);
92,700✔
122
    Array::set(0, RefOrTagged::make_tagged(0)); // Size = 0
92,700✔
123

45,669✔
124
    auto column_initialize = [this](ColKey col_key) {
174,954✔
125
        auto col_ndx = col_key.get_index();
174,954✔
126
        while (size() <= col_ndx.val + 1)
349,908✔
127
            add(0);
174,954✔
128
        auto type = col_key.get_type();
174,954✔
129
        auto attr = col_key.get_attrs();
174,954✔
130
        if (attr.test(col_attr_Collection)) {
174,954✔
131
            ArrayRef arr(m_alloc);
4,314✔
132
            arr.create();
4,314✔
133
            arr.set_parent(this, col_ndx.val + s_first_col_index);
4,314✔
134
            arr.update_parent();
4,314✔
135
            return IteratorControl::AdvanceToNext;
4,314✔
136
        }
4,314✔
137
        switch (type) {
170,640✔
138
            case col_type_Int:
102,417✔
139
                if (attr.test(col_attr_Nullable)) {
102,417✔
140
                    do_create<ArrayIntNull>(col_key);
7,434✔
141
                }
7,434✔
142
                else {
94,983✔
143
                    do_create<ArrayInteger>(col_key);
94,983✔
144
                }
94,983✔
145
                break;
102,417✔
146
            case col_type_Bool:
699✔
147
                do_create<ArrayBoolNull>(col_key);
699✔
148
                break;
699✔
149
            case col_type_Float:
1,116✔
150
                do_create<ArrayFloatNull>(col_key);
1,116✔
151
                break;
1,116✔
152
            case col_type_Double:
5,796✔
153
                do_create<ArrayDoubleNull>(col_key);
5,796✔
154
                break;
5,796✔
155
            case col_type_String: {
44,496✔
156
                if (m_tree_top.is_string_enum_type(col_ndx)) {
44,496✔
157
                    do_create<ArrayInteger>(col_key);
5,340✔
158
                }
5,340✔
159
                else {
39,156✔
160
                    do_create<ArrayString>(col_key);
39,156✔
161
                }
39,156✔
162
                break;
44,496✔
163
            }
×
164
            case col_type_Binary:
5,139✔
165
                do_create<ArrayBinary>(col_key);
5,139✔
166
                break;
5,139✔
167
            case col_type_Mixed:
72✔
168
                do_create<ArrayMixed>(col_key);
72✔
169
                break;
72✔
170
            case col_type_Timestamp:
2,292✔
171
                do_create<ArrayTimestamp>(col_key);
2,292✔
172
                break;
2,292✔
173
            case col_type_Decimal:
276✔
174
                do_create<ArrayDecimal128>(col_key);
276✔
175
                break;
276✔
176
            case col_type_ObjectId:
2,712✔
177
                do_create<ArrayObjectIdNull>(col_key);
2,712✔
178
                break;
2,712✔
179
            case col_type_UUID:
408✔
180
                do_create<ArrayUUIDNull>(col_key);
408✔
181
                break;
408✔
182
            case col_type_Link:
2,172✔
183
                do_create<ArrayKey>(col_key);
2,172✔
184
                break;
2,172✔
185
            case col_type_TypedLink:
6✔
186
                do_create<ArrayTypedLink>(col_key);
6✔
187
                break;
6✔
188
            case col_type_BackLink:
3,042✔
189
                do_create<ArrayBacklink>(col_key);
3,042✔
190
                break;
3,042✔
191
            default:
✔
192
                REALM_UNREACHABLE();
×
193
        }
170,640✔
194
        return IteratorControl::AdvanceToNext;
170,640✔
195
    };
170,640✔
196
    m_tree_top.m_owner->for_each_and_every_column(column_initialize);
92,700✔
197

45,669✔
198
    // By specifying the minimum size, we ensure that the array has a capacity
45,669✔
199
    // to hold m_size 64 bit refs.
45,669✔
200
    ensure_size(m_size * 8);
92,700✔
201
    // "ensure_size" may COW, but as array is just created, it has no parents, so
45,669✔
202
    // failing to update parent is not an error.
45,669✔
203
    clear_missing_parent_update();
92,700✔
204
}
92,700✔
205

206
void Cluster::init(MemRef mem)
207
{
219,657,855✔
208
    Array::init_from_mem(mem);
219,657,855✔
209
    auto rot = Array::get_as_ref_or_tagged(0);
219,657,855✔
210
    if (rot.is_tagged()) {
219,657,855✔
211
        m_keys.detach();
153,192,027✔
212
    }
153,192,027✔
213
    else {
66,465,828✔
214
        m_keys.init_from_ref(rot.get_as_ref());
66,465,828✔
215
    }
66,465,828✔
216
}
219,657,855✔
217

218
void Cluster::update_from_parent() noexcept
219
{
730,998✔
220
    Array::update_from_parent();
730,998✔
221
    auto rot = Array::get_as_ref_or_tagged(0);
730,998✔
222
    if (!rot.is_tagged()) {
730,998✔
223
        m_keys.update_from_parent();
21,210✔
224
    }
21,210✔
225
}
730,998✔
226

227
MemRef Cluster::ensure_writeable(ObjKey)
228
{
×
229
    // By specifying the minimum size, we ensure that the array has a capacity
230
    // to hold m_size 64 bit refs.
231
    copy_on_write(8 * m_size);
×
232

233
    return get_mem();
×
234
}
×
235

236
void Cluster::update_ref_in_parent(ObjKey, ref_type)
237
{
×
238
    REALM_UNREACHABLE();
×
239
}
×
240

241
size_t Cluster::node_size_from_header(Allocator& alloc, const char* header)
242
{
50,871,396✔
243
    auto rot = Array::get_as_ref_or_tagged(header, s_key_ref_or_size_index);
50,871,396✔
244
    if (rot.is_tagged()) {
50,871,396✔
245
        return size_t(rot.get_as_int());
50,604,024✔
246
    }
50,604,024✔
247
    else {
267,372✔
248
        return Array::get_size_from_header(alloc.translate(rot.get_as_ref()));
267,372✔
249
    }
267,372✔
250
}
50,871,396✔
251

252
template <class T>
253
inline void Cluster::set_spec(T&, ColKey::Idx) const
254
{
40,520,148✔
255
}
40,520,148✔
256

257
template <>
258
inline void Cluster::set_spec(ArrayString& arr, ColKey::Idx col_ndx) const
259
{
5,018,898✔
260
    m_tree_top.set_spec(arr, col_ndx);
5,018,898✔
261
}
5,018,898✔
262

263
template <class T>
264
inline void Cluster::do_insert_row(size_t ndx, ColKey col, Mixed init_val, bool nullable)
265
{
32,154,729✔
266
    using U = typename util::RemoveOptional<typename T::value_type>::type;
32,154,729✔
267

16,014,468✔
268
    T arr(m_alloc);
32,154,729✔
269
    auto col_ndx = col.get_index();
32,154,729✔
270
    arr.set_parent(this, col_ndx.val + s_first_col_index);
32,154,729✔
271
    set_spec<T>(arr, col_ndx);
32,154,729✔
272
    arr.init_from_parent();
32,154,729✔
273
    if (init_val.is_null()) {
32,154,729✔
274
        arr.insert(ndx, T::default_value(nullable));
31,505,337✔
275
    }
31,505,337✔
276
    else {
649,392✔
277
        arr.insert(ndx, init_val.get<U>());
649,392✔
278
    }
649,392✔
279
}
32,154,729✔
280

281
inline void Cluster::do_insert_key(size_t ndx, ColKey col_key, Mixed init_val, ObjKey origin_key)
282
{
254,964✔
283
    ObjKey target_key = init_val.is_null() ? ObjKey{} : init_val.get<ObjKey>();
254,958✔
284
    ArrayKey arr(m_alloc);
254,964✔
285
    auto col_ndx = col_key.get_index();
254,964✔
286
    arr.set_parent(this, col_ndx.val + s_first_col_index);
254,964✔
287
    arr.init_from_parent();
254,964✔
288
    arr.insert(ndx, target_key);
254,964✔
289

128,259✔
290
    // Insert backlink if link is not null
128,259✔
291
    if (target_key) {
254,964✔
292
        const Table* origin_table = m_tree_top.get_owning_table();
12✔
293
        ColKey opp_col = origin_table->get_opposite_column(col_key);
12✔
294
        TableRef opp_table = origin_table->get_opposite_table(col_key);
12✔
295
        Obj target_obj = opp_table->get_object(target_key);
12✔
296
        target_obj.add_backlink(opp_col, origin_key);
12✔
297
    }
12✔
298
}
254,964✔
299

300
inline void Cluster::do_insert_mixed(size_t ndx, ColKey col_key, Mixed init_value, ObjKey origin_key)
301
{
13,062✔
302
    ArrayMixed arr(m_alloc);
13,062✔
303
    arr.set_parent(this, col_key.get_index().val + s_first_col_index);
13,062✔
304
    arr.init_from_parent();
13,062✔
305
    arr.insert(ndx, init_value);
13,062✔
306

6,381✔
307
    // Insert backlink if needed
6,381✔
308
    if (init_value.is_type(type_TypedLink)) {
13,062✔
309
        // In case we are inserting in a Dictionary cluster, the backlink will
9✔
310
        // be handled in Dictionary::insert function
9✔
311
        if (Table* origin_table = const_cast<Table*>(m_tree_top.get_owning_table())) {
18✔
312
            if (origin_table->is_asymmetric()) {
18✔
313
                throw IllegalOperation("Object value not supported in asymmetric table");
6✔
314
            }
6✔
315
            ObjLink link = init_value.get<ObjLink>();
12✔
316
            auto target_table = origin_table->get_parent_group()->get_table(link.get_table_key());
12✔
317
            if (target_table->is_asymmetric()) {
12✔
318
                throw IllegalOperation("Ephemeral object value not supported");
6✔
319
            }
6✔
320
            ColKey backlink_col_key = target_table->find_or_add_backlink_column(col_key, origin_table->get_key());
6✔
321
            target_table->get_object(link.get_obj_key()).add_backlink(backlink_col_key, origin_key);
6✔
322
        }
6✔
323
    }
18✔
324
}
13,062✔
325

326
inline void Cluster::do_insert_link(size_t ndx, ColKey col_key, Mixed init_val, ObjKey origin_key)
327
{
66✔
328
    ObjLink target_link = init_val.is_null() ? ObjLink{} : init_val.get<ObjLink>();
60✔
329
    ArrayTypedLink arr(m_alloc);
66✔
330
    auto col_ndx = col_key.get_index();
66✔
331
    arr.set_parent(this, col_ndx.val + s_first_col_index);
66✔
332
    arr.init_from_parent();
66✔
333
    arr.insert(ndx, target_link);
66✔
334

33✔
335
    // Insert backlink if link is not null
33✔
336
    if (target_link) {
66✔
337
        Table* origin_table = const_cast<Table*>(m_tree_top.get_owning_table());
12✔
338
        auto target_table = origin_table->get_parent_group()->get_table(target_link.get_table_key());
12✔
339

6✔
340
        ColKey backlink_col_key = target_table->find_or_add_backlink_column(col_key, origin_table->get_key());
12✔
341
        target_table->get_object(target_link.get_obj_key()).add_backlink(backlink_col_key, origin_key);
12✔
342
    }
12✔
343
}
66✔
344

345
void Cluster::insert_row(size_t ndx, ObjKey k, const FieldValues& init_values)
346
{
23,144,808✔
347
    // Ensure the cluster array is big enough to hold 64 bit values.
11,548,035✔
348
    copy_on_write(m_size * 8);
23,144,808✔
349

11,548,035✔
350
    if (m_keys.is_attached()) {
23,144,808✔
351
        m_keys.insert(ndx, k.value);
1,705,152✔
352
    }
1,705,152✔
353
    else {
21,439,656✔
354
        Array::set(s_key_ref_or_size_index, Array::get(s_key_ref_or_size_index) + 2); // Increments size by 1
21,439,656✔
355
    }
21,439,656✔
356

11,548,035✔
357
    auto val = init_values.begin();
23,144,808✔
358
    auto insert_in_column = [&](ColKey col_key) {
33,922,515✔
359
        auto col_ndx = col_key.get_index();
33,922,515✔
360
        auto attr = col_key.get_attrs();
33,922,515✔
361
        Mixed init_value;
33,922,515✔
362
        // init_values must be sorted in col_ndx order - this is ensured by ClustTree::insert()
16,844,475✔
363
        if (val != init_values.end() && val->col_key.get_index().val == col_ndx.val) {
33,922,515✔
364
            init_value = val->value;
610,827✔
365
            ++val;
610,827✔
366
        }
610,827✔
367

16,844,475✔
368
        auto type = col_key.get_type();
33,922,515✔
369
        if (attr.test(col_attr_Collection)) {
33,922,515✔
370
            REALM_ASSERT(init_value.is_null());
676,512✔
371
            ArrayRef arr(m_alloc);
676,512✔
372
            arr.set_parent(this, col_ndx.val + s_first_col_index);
676,512✔
373
            arr.init_from_parent();
676,512✔
374
            arr.insert(ndx, 0);
676,512✔
375
            return IteratorControl::AdvanceToNext;
676,512✔
376
        }
676,512✔
377

16,508,040✔
378
        bool nullable = attr.test(col_attr_Nullable);
33,246,003✔
379
        switch (type) {
33,246,003✔
380
            case col_type_Int:
25,524,645✔
381
                if (attr.test(col_attr_Nullable)) {
25,524,645✔
382
                    do_insert_row<ArrayIntNull>(ndx, col_key, init_value, nullable);
1,389,207✔
383
                }
1,389,207✔
384
                else {
24,135,438✔
385
                    do_insert_row<ArrayInteger>(ndx, col_key, init_value, nullable);
24,135,438✔
386
                }
24,135,438✔
387
                break;
25,524,645✔
388
            case col_type_Bool:
193,323✔
389
                do_insert_row<ArrayBoolNull>(ndx, col_key, init_value, nullable);
193,323✔
390
                break;
193,323✔
391
            case col_type_Float:
325,956✔
392
                do_insert_row<ArrayFloatNull>(ndx, col_key, init_value, nullable);
325,956✔
393
                break;
325,956✔
394
            case col_type_Double:
1,492,431✔
395
                do_insert_row<ArrayDoubleNull>(ndx, col_key, init_value, nullable);
1,492,431✔
396
                break;
1,492,431✔
397
            case col_type_String:
2,720,445✔
398
                do_insert_row<ArrayString>(ndx, col_key, init_value, nullable);
2,720,445✔
399
                break;
2,720,445✔
400
            case col_type_Binary:
1,321,293✔
401
                do_insert_row<ArrayBinary>(ndx, col_key, init_value, nullable);
1,321,293✔
402
                break;
1,321,293✔
403
            case col_type_Mixed: {
13,062✔
404
                do_insert_mixed(ndx, col_key, init_value, ObjKey(k.value + get_offset()));
13,062✔
405
                break;
13,062✔
406
            }
×
407
            case col_type_Timestamp:
220,818✔
408
                do_insert_row<ArrayTimestamp>(ndx, col_key, init_value, nullable);
220,818✔
409
                break;
220,818✔
410
            case col_type_Decimal:
75,612✔
411
                do_insert_row<ArrayDecimal128>(ndx, col_key, init_value, nullable);
75,612✔
412
                break;
75,612✔
413
            case col_type_ObjectId:
162,609✔
414
                do_insert_row<ArrayObjectIdNull>(ndx, col_key, init_value, nullable);
162,609✔
415
                break;
162,609✔
416
            case col_type_UUID:
115,830✔
417
                do_insert_row<ArrayUUIDNull>(ndx, col_key, init_value, nullable);
115,830✔
418
                break;
115,830✔
419
            case col_type_Link:
254,964✔
420
                do_insert_key(ndx, col_key, init_value, ObjKey(k.value + get_offset()));
254,964✔
421
                break;
254,964✔
422
            case col_type_TypedLink:
66✔
423
                do_insert_link(ndx, col_key, init_value, ObjKey(k.value + get_offset()));
66✔
424
                break;
66✔
425
            case col_type_BackLink: {
956,892✔
426
                ArrayBacklink arr(m_alloc);
956,892✔
427
                arr.set_parent(this, col_ndx.val + s_first_col_index);
956,892✔
428
                arr.init_from_parent();
956,892✔
429
                arr.insert(ndx, 0);
956,892✔
430
                break;
956,892✔
431
            }
×
432
            default:
✔
433
                REALM_ASSERT(false);
×
434
                break;
×
435
        }
33,203,385✔
436
        return IteratorControl::AdvanceToNext;
33,203,385✔
437
    };
33,203,385✔
438
    m_tree_top.m_owner->for_each_and_every_column(insert_in_column);
23,144,808✔
439
}
23,144,808✔
440

441
template <class T>
442
inline void Cluster::do_move(size_t ndx, ColKey col_key, Cluster* to)
443
{
21,732✔
444
    auto col_ndx = col_key.get_index().val + s_first_col_index;
21,732✔
445
    T src(m_alloc);
21,732✔
446
    src.set_parent(this, col_ndx);
21,732✔
447
    src.init_from_parent();
21,732✔
448

10,878✔
449
    T dst(m_alloc);
21,732✔
450
    dst.set_parent(to, col_ndx);
21,732✔
451
    dst.init_from_parent();
21,732✔
452

10,878✔
453
    src.move(dst, ndx);
21,732✔
454
}
21,732✔
455

456
void Cluster::move(size_t ndx, ClusterNode* new_node, int64_t offset)
457
{
11,430✔
458
    auto new_leaf = static_cast<Cluster*>(new_node);
11,430✔
459

5,718✔
460
    auto move_from_column = [&](ColKey col_key) {
21,732✔
461
        auto attr = col_key.get_attrs();
21,732✔
462
        auto type = col_key.get_type();
21,732✔
463

10,878✔
464
        if (attr.test(col_attr_Collection)) {
21,732✔
465
            do_move<ArrayRef>(ndx, col_key, new_leaf);
30✔
466
            return IteratorControl::AdvanceToNext;
30✔
467
        }
30✔
468

10,863✔
469
        switch (type) {
21,702✔
470
            case col_type_Int:
20,838✔
471
                if (attr.test(col_attr_Nullable)) {
20,838✔
472
                    do_move<ArrayIntNull>(ndx, col_key, new_leaf);
9,423✔
473
                }
9,423✔
474
                else {
11,415✔
475
                    do_move<ArrayInteger>(ndx, col_key, new_leaf);
11,415✔
476
                }
11,415✔
477
                break;
20,838✔
478
            case col_type_Bool:
36✔
479
                do_move<ArrayBoolNull>(ndx, col_key, new_leaf);
36✔
480
                break;
36✔
481
            case col_type_Float:
36✔
482
                do_move<ArrayFloat>(ndx, col_key, new_leaf);
36✔
483
                break;
36✔
484
            case col_type_Double:
36✔
485
                do_move<ArrayDouble>(ndx, col_key, new_leaf);
36✔
486
                break;
36✔
487
            case col_type_String: {
36✔
488
                if (m_tree_top.is_string_enum_type(col_key.get_index()))
36✔
489
                    do_move<ArrayInteger>(ndx, col_key, new_leaf);
×
490
                else
36✔
491
                    do_move<ArrayString>(ndx, col_key, new_leaf);
36✔
492
                break;
36✔
493
            }
×
494
            case col_type_Binary:
36✔
495
                do_move<ArrayBinary>(ndx, col_key, new_leaf);
36✔
496
                break;
36✔
497
            case col_type_Mixed:
✔
498
                do_move<ArrayMixed>(ndx, col_key, new_leaf);
×
499
                break;
×
500
            case col_type_Timestamp:
36✔
501
                do_move<ArrayTimestamp>(ndx, col_key, new_leaf);
36✔
502
                break;
36✔
503
            case col_type_Decimal:
36✔
504
                do_move<ArrayDecimal128>(ndx, col_key, new_leaf);
36✔
505
                break;
36✔
506
            case col_type_ObjectId:
36✔
507
                do_move<ArrayObjectIdNull>(ndx, col_key, new_leaf);
36✔
508
                break;
36✔
509
            case col_type_UUID:
120✔
510
                do_move<ArrayUUIDNull>(ndx, col_key, new_leaf);
120✔
511
                break;
120✔
512
            case col_type_Link:
✔
513
                do_move<ArrayKey>(ndx, col_key, new_leaf);
×
514
                break;
×
515
            case col_type_TypedLink:
✔
516
                do_move<ArrayTypedLink>(ndx, col_key, new_leaf);
×
517
                break;
×
518
            case col_type_BackLink:
456✔
519
                do_move<ArrayBacklink>(ndx, col_key, new_leaf);
456✔
520
                break;
456✔
521
            default:
✔
522
                REALM_ASSERT(false);
×
523
                break;
×
524
        }
21,702✔
525
        return IteratorControl::AdvanceToNext;
21,702✔
526
    };
21,702✔
527
    m_tree_top.m_owner->for_each_and_every_column(move_from_column);
11,430✔
528
    for (size_t i = ndx; i < m_keys.size(); i++) {
1,236,111✔
529
        new_leaf->m_keys.add(m_keys.get(i) - offset);
1,224,681✔
530
    }
1,224,681✔
531
    m_keys.truncate(ndx);
11,430✔
532
}
11,430✔
533

534
Cluster::~Cluster() {}
219,795,747✔
535

536
ColKey Cluster::get_col_key(size_t ndx_in_parent) const
537
{
10,488✔
538
    ColKey::Idx col_ndx{unsigned(ndx_in_parent - 1)}; // <- leaf_index here. Opaque.
10,488✔
539
    auto col_key = get_owning_table()->leaf_ndx2colkey(col_ndx);
10,488✔
540
    REALM_ASSERT(col_key.get_index().val == col_ndx.val);
10,488✔
541
    return col_key;
10,488✔
542
}
10,488✔
543

544
void Cluster::ensure_general_form()
545
{
83,382✔
546
    if (!m_keys.is_attached()) {
83,382✔
547
        size_t current_size = get_size_in_compact_form();
70,170✔
548
        m_keys.create(current_size, 255);
70,170✔
549
        m_keys.update_parent();
70,170✔
550
        for (size_t i = 0; i < current_size; i++) {
5,497,008✔
551
            m_keys.set(i, i);
5,426,838✔
552
        }
5,426,838✔
553
    }
70,170✔
554
}
83,382✔
555

556
template <class T>
557
inline void Cluster::do_insert_column(ColKey col_key, bool nullable)
558
{
800,298✔
559
    size_t sz = node_size();
800,298✔
560

394,551✔
561
    T arr(m_alloc);
800,298✔
562
    arr.create();
800,298✔
563
    auto val = T::default_value(nullable);
800,298✔
564
    for (size_t i = 0; i < sz; i++) {
7,092,525✔
565
        arr.add(val);
6,292,227✔
566
    }
6,292,227✔
567
    auto col_ndx = col_key.get_index();
800,298✔
568
    unsigned ndx = col_ndx.val + s_first_col_index;
800,298✔
569

394,551✔
570
    // Fill up if indexes are not consecutive
394,551✔
571
    while (size() < ndx)
800,298✔
572
        Array::add(0);
×
573

394,551✔
574
    if (ndx == size())
800,298✔
575
        Array::insert(ndx, from_ref(arr.get_ref()));
800,211✔
576
    else
87✔
577
        Array::set(ndx, from_ref(arr.get_ref()));
87✔
578
}
800,298✔
579

580
void Cluster::insert_column(ColKey col_key)
581
{
1,034,727✔
582
    auto attr = col_key.get_attrs();
1,034,727✔
583
    auto type = col_key.get_type();
1,034,727✔
584
    if (attr.test(col_attr_Collection)) {
1,034,727✔
585
        size_t sz = node_size();
234,426✔
586

116,793✔
587
        ArrayRef arr(m_alloc);
234,426✔
588
        arr.create(sz);
234,426✔
589
        auto col_ndx = col_key.get_index();
234,426✔
590
        unsigned idx = col_ndx.val + s_first_col_index;
234,426✔
591
        if (idx == size())
234,426✔
592
            Array::insert(idx, from_ref(arr.get_ref()));
234,426✔
UNCOV
593
        else
×
UNCOV
594
            Array::set(idx, from_ref(arr.get_ref()));
×
595
        return;
234,426✔
596
    }
234,426✔
597
    bool nullable = attr.test(col_attr_Nullable);
800,301✔
598
    switch (type) {
800,301✔
599
        case col_type_Int:
312,543✔
600
            if (nullable) {
312,543✔
601
                do_insert_column<ArrayIntNull>(col_key, nullable);
24,744✔
602
            }
24,744✔
603
            else {
287,799✔
604
                do_insert_column<ArrayInteger>(col_key, nullable);
287,799✔
605
            }
287,799✔
606
            break;
312,543✔
607
        case col_type_Bool:
5,640✔
608
            do_insert_column<ArrayBoolNull>(col_key, nullable);
5,640✔
609
            break;
5,640✔
610
        case col_type_Float:
6,363✔
611
            do_insert_column<ArrayFloatNull>(col_key, nullable);
6,363✔
612
            break;
6,363✔
613
        case col_type_Double:
6,975✔
614
            do_insert_column<ArrayDoubleNull>(col_key, nullable);
6,975✔
615
            break;
6,975✔
616
        case col_type_String:
264,834✔
617
            do_insert_column<ArrayString>(col_key, nullable);
264,834✔
618
            break;
264,834✔
619
        case col_type_Binary:
6,306✔
620
            do_insert_column<ArrayBinary>(col_key, nullable);
6,306✔
621
            break;
6,306✔
622
        case col_type_Mixed:
3,666✔
623
            do_insert_column<ArrayMixed>(col_key, nullable);
3,666✔
624
            break;
3,666✔
625
        case col_type_Timestamp:
20,823✔
626
            do_insert_column<ArrayTimestamp>(col_key, nullable);
20,823✔
627
            break;
20,823✔
628
        case col_type_Decimal:
4,938✔
629
            do_insert_column<ArrayDecimal128>(col_key, nullable);
4,938✔
630
            break;
4,938✔
631
        case col_type_ObjectId:
43,317✔
632
            do_insert_column<ArrayObjectIdNull>(col_key, nullable);
43,317✔
633
            break;
43,317✔
634
        case col_type_UUID:
5,526✔
635
            do_insert_column<ArrayUUIDNull>(col_key, nullable);
5,526✔
636
            break;
5,526✔
637
        case col_type_Link:
29,763✔
638
            do_insert_column<ArrayKey>(col_key, nullable);
29,763✔
639
            break;
29,763✔
640
        case col_type_TypedLink:
36✔
641
            do_insert_column<ArrayTypedLink>(col_key, nullable);
36✔
642
            break;
36✔
643
        case col_type_BackLink:
89,568✔
644
            do_insert_column<ArrayBacklink>(col_key, nullable);
89,568✔
645
            break;
89,568✔
646
        default:
✔
647
            REALM_UNREACHABLE();
×
648
            break;
×
649
    }
800,301✔
650
}
800,301✔
651

652
void Cluster::remove_column(ColKey col_key)
653
{
27,171✔
654
    auto col_ndx = col_key.get_index();
27,171✔
655
    unsigned idx = col_ndx.val + s_first_col_index;
27,171✔
656
    ref_type ref = to_ref(Array::get(idx));
27,171✔
657
    if (ref != 0) {
27,171✔
658
        Array::destroy_deep(ref, m_alloc);
27,171✔
659
    }
27,171✔
660
    if (idx == size() - 1)
27,171✔
661
        Array::erase(idx);
26,427✔
662
    else
744✔
663
        Array::set(idx, 0);
744✔
664
}
27,171✔
665

666
ref_type Cluster::insert(ObjKey k, const FieldValues& init_values, ClusterNode::State& state)
667
{
23,139,891✔
668
    int64_t current_key_value = -1;
23,139,891✔
669
    size_t sz;
23,139,891✔
670
    size_t ndx;
23,139,891✔
671
    ref_type ret = 0;
23,139,891✔
672

11,549,376✔
673
    auto on_error = [&] {
11,549,382✔
674
        throw KeyAlreadyUsed(
18✔
675
            util::format("When inserting key '%1' in '%2'", k.value, get_owning_table()->get_name()));
18✔
676
    };
18✔
677

11,549,376✔
678
    if (m_keys.is_attached()) {
23,139,891✔
679
        sz = m_keys.size();
1,677,351✔
680
        ndx = m_keys.lower_bound(uint64_t(k.value));
1,677,351✔
681
        if (ndx < sz) {
1,677,351✔
682
            current_key_value = m_keys.get(ndx);
620,904✔
683
            if (k.value == current_key_value) {
620,904✔
684
                on_error();
18✔
685
            }
18✔
686
        }
620,904✔
687
    }
1,677,351✔
688
    else {
21,462,540✔
689
        sz = size_t(Array::get(s_key_ref_or_size_index)) >> 1; // Size is stored as tagged integer
21,462,540✔
690
        if (uint64_t(k.value) < sz) {
21,462,540✔
691
            on_error();
×
692
        }
×
693
        // Key value is bigger than all other values, should be put last
10,708,593✔
694
        ndx = sz;
21,462,540✔
695
        if (uint64_t(k.value) > sz && sz < cluster_node_size) {
21,462,540✔
696
            ensure_general_form();
33,453✔
697
        }
33,453✔
698
    }
21,462,540✔
699

11,549,376✔
700
    REALM_ASSERT_DEBUG(sz <= cluster_node_size);
23,139,891✔
701
    if (REALM_LIKELY(sz < cluster_node_size)) {
23,139,891✔
702
        insert_row(ndx, k, init_values); // Throws
23,018,253✔
703
        state.mem = get_mem();
23,018,253✔
704
        state.index = ndx;
23,018,253✔
705
    }
23,018,253✔
706
    else {
121,638✔
707
        // Split leaf node
85,077✔
708
        Cluster new_leaf(0, m_alloc, m_tree_top);
121,638✔
709
        new_leaf.create();
121,638✔
710
        if (ndx == sz) {
126,963✔
711
            new_leaf.insert_row(0, ObjKey(0), init_values); // Throws
83,730✔
712
            state.split_key = k.value;
83,730✔
713
            state.mem = new_leaf.get_mem();
83,730✔
714
            state.index = 0;
83,730✔
715
        }
83,730✔
716
        else {
2,147,526,880✔
717
            // Current cluster must be in general form to get here
43,233✔
718
            REALM_ASSERT_DEBUG(m_keys.is_attached());
2,147,526,880✔
719
            new_leaf.ensure_general_form();
2,147,526,880✔
720
            move(ndx, &new_leaf, current_key_value);
2,147,526,880✔
721
            insert_row(ndx, k, init_values); // Throws
2,147,526,880✔
722
            state.mem = get_mem();
2,147,526,880✔
723
            state.split_key = current_key_value;
2,147,526,880✔
724
            state.index = ndx;
2,147,526,880✔
725
        }
2,147,526,880✔
726
        ret = new_leaf.get_ref();
121,638✔
727
    }
121,638✔
728

11,549,376✔
729
    return ret;
23,139,891✔
730
}
23,139,891✔
731

732
bool Cluster::try_get(ObjKey k, ClusterNode::State& state) const noexcept
733
{
177,594,150✔
734
    state.mem = get_mem();
177,594,150✔
735
    if (m_keys.is_attached()) {
177,594,150✔
736
        state.index = m_keys.lower_bound(uint64_t(k.value));
54,122,259✔
737
        return state.index != m_keys.size() && m_keys.get(state.index) == uint64_t(k.value);
54,122,259✔
738
    }
54,122,259✔
739
    else {
123,471,891✔
740
        if (uint64_t(k.value) < uint64_t(Array::get(s_key_ref_or_size_index) >> 1)) {
123,471,891✔
741
            state.index = size_t(k.value);
106,760,973✔
742
            return true;
106,760,973✔
743
        }
106,760,973✔
744
    }
16,710,918✔
745
    return false;
16,710,918✔
746
}
16,710,918✔
747

748
ObjKey Cluster::get(size_t ndx, ClusterNode::State& state) const
749
{
4,693,971✔
750
    state.index = ndx;
4,693,971✔
751
    state.mem = get_mem();
4,693,971✔
752
    return get_real_key(ndx);
4,693,971✔
753
}
4,693,971✔
754

755
template <class T>
756
inline void Cluster::do_erase(size_t ndx, ColKey col_key)
757
{
6,679,314✔
758
    auto col_ndx = col_key.get_index();
6,679,314✔
759
    T values(m_alloc);
6,679,314✔
760
    values.set_parent(this, col_ndx.val + s_first_col_index);
6,679,314✔
761
    set_spec<T>(values, col_ndx);
6,679,314✔
762
    values.init_from_parent();
6,679,314✔
763
    ObjLink link;
6,679,314✔
764
    if constexpr (std::is_same_v<T, ArrayTypedLink>) {
6,679,314✔
765
        link = values.get(ndx);
6✔
766
    }
6✔
767
    if constexpr (std::is_same_v<T, ArrayMixed>) {
6,679,314✔
768
        Mixed value = values.get(ndx);
609✔
769
        if (value.is_type(type_TypedLink)) {
609✔
770
            link = value.get<ObjLink>();
24✔
771
        }
24✔
772
    }
609✔
773
    if (link) {
6,679,314✔
774
        if (const Table* origin_table = m_tree_top.get_owning_table()) {
30!
775
            auto target_obj = origin_table->get_parent_group()->get_object(link);
30✔
776

15✔
777
            ColKey backlink_col_key = target_obj.get_table()->find_backlink_column(col_key, origin_table->get_key());
30✔
778
            REALM_ASSERT(backlink_col_key);
30!
779
            target_obj.remove_one_backlink(backlink_col_key, get_real_key(ndx)); // Throws
30✔
780
        }
30✔
781
    }
30✔
782
    values.erase(ndx);
6,679,314✔
783
}
6,679,314✔
784

785
inline void Cluster::do_erase_key(size_t ndx, ColKey col_key, CascadeState& state)
786
{
7,143✔
787
    ArrayKey values(m_alloc);
7,143✔
788
    auto col_ndx = col_key.get_index();
7,143✔
789
    values.set_parent(this, col_ndx.val + s_first_col_index);
7,143✔
790
    values.init_from_parent();
7,143✔
791

3,567✔
792
    ObjKey key = values.get(ndx);
7,143✔
793
    if (key != null_key) {
7,143✔
794
        remove_backlinks(get_real_key(ndx), col_key, std::vector<ObjKey>{key}, state);
4,551✔
795
    }
4,551✔
796
    values.erase(ndx);
7,143✔
797
}
7,143✔
798

799
size_t Cluster::get_ndx(ObjKey k, size_t ndx) const noexcept
800
{
10,049,493✔
801
    size_t index;
10,049,493✔
802
    if (m_keys.is_attached()) {
10,049,493✔
803
        index = m_keys.lower_bound(uint64_t(k.value));
9,884,805✔
804
        if (index == m_keys.size() || m_keys.get(index) != uint64_t(k.value)) {
9,884,937✔
805
            return realm::npos;
18✔
806
        }
18✔
807
    }
164,688✔
808
    else {
164,688✔
809
        index = size_t(k.value);
164,688✔
810
        if (index >= get_as_ref_or_tagged(s_key_ref_or_size_index).get_as_int()) {
164,688✔
811
            return realm::npos;
×
812
        }
×
813
    }
10,049,475✔
814
    return index + ndx;
10,049,475✔
815
}
10,049,475✔
816

817
size_t Cluster::erase(ObjKey key, CascadeState& state)
818
{
5,024,682✔
819
    size_t ndx = get_ndx(key, 0);
5,024,682✔
820
    if (ndx == realm::npos)
5,024,682✔
821
        throw KeyNotFound(util::format("When erasing key '%1' in '%2'", key.value, get_owning_table()->get_name()));
18✔
822
    std::vector<ColKey> backlink_column_keys;
5,024,664✔
823

2,513,472✔
824
    auto erase_in_column = [&](ColKey col_key) {
6,739,047✔
825
        auto col_type = col_key.get_type();
6,739,047✔
826
        auto attr = col_key.get_attrs();
6,739,047✔
827
        if (attr.test(col_attr_Collection)) {
6,739,047✔
828
            auto col_ndx = col_key.get_index();
53,283✔
829
            ArrayRef values(m_alloc);
53,283✔
830
            values.set_parent(this, col_ndx.val + s_first_col_index);
53,283✔
831
            values.init_from_parent();
53,283✔
832
            ref_type ref = values.get(ndx);
53,283✔
833

26,661✔
834
            if (ref) {
53,283✔
835
                const Table* origin_table = m_tree_top.get_owning_table();
18,375✔
836
                if (attr.test(col_attr_Dictionary)) {
18,375✔
837
                    if (col_type == col_type_Mixed || col_type == col_type_Link) {
2,643✔
838
                        Obj obj(origin_table->m_own_ref, get_mem(), key, ndx);
348✔
839
                        const Dictionary dict = obj.get_dictionary(col_key);
348✔
840
                        dict.remove_backlinks(state);
348✔
841
                    }
348✔
842
                }
2,643✔
843
                else if (col_type == col_type_LinkList || col_type == col_type_Link) {
15,732✔
844
                    BPlusTree<ObjKey> links(m_alloc);
3,486✔
845
                    links.init_from_ref(ref);
3,486✔
846
                    if (links.size() > 0) {
3,486✔
847
                        remove_backlinks(ObjKey(key.value + m_offset), col_key, links.get_all(), state);
1,353✔
848
                    }
1,353✔
849
                }
3,486✔
850
                else if (col_type == col_type_TypedLink) {
12,246✔
851
                    BPlusTree<ObjLink> links(m_alloc);
6✔
852
                    links.init_from_ref(ref);
6✔
853
                    for (size_t i = 0; i < links.size(); i++) {
12✔
854
                        ObjLink link = links.get(i);
6✔
855
                        auto target_obj = origin_table->get_parent_group()->get_object(link);
6✔
856
                        ColKey backlink_col_key =
6✔
857
                            target_obj.get_table()->find_backlink_column(col_key, origin_table->get_key());
6✔
858
                        target_obj.remove_one_backlink(backlink_col_key, ObjKey(key.value + m_offset));
6✔
859
                    }
6✔
860
                }
6✔
861
                else if (col_type == col_type_Mixed) {
12,240✔
862
                    BPlusTree<Mixed> list(m_alloc);
210✔
863
                    list.init_from_ref(ref);
210✔
864
                    for (size_t i = 0; i < list.size(); i++) {
978✔
865
                        Mixed val = list.get(i);
768✔
866
                        if (val.is_type(type_TypedLink)) {
768✔
867
                            ObjLink link = val.get<ObjLink>();
540✔
868
                            auto target_obj = origin_table->get_parent_group()->get_object(link);
540✔
869
                            ColKey backlink_col_key =
540✔
870
                                target_obj.get_table()->find_backlink_column(col_key, origin_table->get_key());
540✔
871
                            target_obj.remove_one_backlink(backlink_col_key, ObjKey(key.value + m_offset));
540✔
872
                        }
540✔
873
                    }
768✔
874
                }
210✔
875
                Array::destroy_deep(ref, m_alloc);
18,375✔
876
            }
18,375✔
877

26,661✔
878
            values.erase(ndx);
53,283✔
879

26,661✔
880
            return IteratorControl::AdvanceToNext;
53,283✔
881
        }
53,283✔
882

3,343,389✔
883
        switch (col_type) {
6,685,764✔
884
            case col_type_Int:
6,016,095✔
885
                if (attr.test(col_attr_Nullable)) {
6,016,095✔
886
                    do_erase<ArrayIntNull>(ndx, col_key);
1,289,091✔
887
                }
1,289,091✔
888
                else {
4,727,004✔
889
                    do_erase<ArrayInteger>(ndx, col_key);
4,727,004✔
890
                }
4,727,004✔
891
                break;
6,016,095✔
892
            case col_type_Bool:
42,513✔
893
                do_erase<ArrayBoolNull>(ndx, col_key);
42,513✔
894
                break;
42,513✔
895
            case col_type_Float:
36,489✔
896
                do_erase<ArrayFloatNull>(ndx, col_key);
36,489✔
897
                break;
36,489✔
898
            case col_type_Double:
36,504✔
899
                do_erase<ArrayDoubleNull>(ndx, col_key);
36,504✔
900
                break;
36,504✔
901
            case col_type_String:
123,825✔
902
                do_erase<ArrayString>(ndx, col_key);
123,825✔
903
                break;
123,825✔
904
            case col_type_Binary:
38,784✔
905
                do_erase<ArrayBinary>(ndx, col_key);
38,784✔
906
                break;
38,784✔
907
            case col_type_Mixed:
609✔
908
                do_erase<ArrayMixed>(ndx, col_key);
609✔
909
                break;
609✔
910
            case col_type_Timestamp:
45,660✔
911
                do_erase<ArrayTimestamp>(ndx, col_key);
45,660✔
912
                break;
45,660✔
913
            case col_type_Decimal:
36,279✔
914
                do_erase<ArrayDecimal128>(ndx, col_key);
36,279✔
915
                break;
36,279✔
916
            case col_type_ObjectId:
39,018✔
917
                do_erase<ArrayObjectIdNull>(ndx, col_key);
39,018✔
918
                break;
39,018✔
919
            case col_type_UUID:
60,285✔
920
                do_erase<ArrayUUIDNull>(ndx, col_key);
60,285✔
921
                break;
60,285✔
922
            case col_type_Link:
7,143✔
923
                do_erase_key(ndx, col_key, state);
7,143✔
924
                break;
7,143✔
925
            case col_type_TypedLink:
6✔
926
                do_erase<ArrayTypedLink>(ndx, col_key);
6✔
927
                break;
6✔
928
            case col_type_BackLink:
203,232✔
929
                if (state.m_mode == CascadeState::Mode::None) {
203,232✔
930
                    do_erase<ArrayBacklink>(ndx, col_key);
176,646✔
931
                }
176,646✔
932
                else {
26,586✔
933
                    // Postpone the deletion of backlink entries or else the
13,272✔
934
                    // checks for if there's any remaining backlinks will
13,272✔
935
                    // check the wrong row for columns which have already
13,272✔
936
                    // had values erased from them.
13,272✔
937
                    backlink_column_keys.push_back(col_key);
26,586✔
938
                }
26,586✔
939
                break;
203,232✔
940
            default:
✔
941
                REALM_ASSERT(false);
×
942
                break;
×
943
        }
6,685,941✔
944
        return IteratorControl::AdvanceToNext;
6,685,941✔
945
    };
6,685,941✔
946
    m_tree_top.m_owner->for_each_and_every_column(erase_in_column);
5,024,664✔
947

2,513,472✔
948
    // Any remaining backlink columns to erase from?
2,513,472✔
949
    for (auto k : backlink_column_keys)
5,024,664✔
950
        do_erase<ArrayBacklink>(ndx, k);
26,586✔
951

2,513,472✔
952
    if (m_keys.is_attached()) {
5,024,664✔
953
        m_keys.erase(ndx);
4,967,655✔
954
    }
4,967,655✔
955
    else {
57,009✔
956
        size_t current_size = get_size_in_compact_form();
57,009✔
957
        if (ndx == current_size - 1) {
57,009✔
958
            // When deleting last, we can still maintain compact form
18,390✔
959
            set(0, RefOrTagged::make_tagged(current_size - 1));
36,975✔
960
        }
36,975✔
961
        else {
20,034✔
962
            ensure_general_form();
20,034✔
963
            m_keys.erase(ndx);
20,034✔
964
        }
20,034✔
965
    }
57,009✔
966

2,513,472✔
967
    return node_size();
5,024,664✔
968
}
5,024,664✔
969

970
void Cluster::nullify_incoming_links(ObjKey key, CascadeState& state)
971
{
4,924,911✔
972
    size_t ndx = get_ndx(key, 0);
4,924,911✔
973
    if (ndx == realm::npos)
4,924,911✔
974
        throw KeyNotFound(util::format("Key '%1' not found in '%2' when nullifying incoming links", key.value,
×
975
                                       get_owning_table()->get_class_name()));
×
976

2,463,570✔
977
    // We must start with backlink columns in case the corresponding link
2,463,570✔
978
    // columns are in the same table so that we can nullify links before
2,463,570✔
979
    // erasing rows in the link columns.
2,463,570✔
980
    //
2,463,570✔
981
    // This phase also generates replication instructions documenting the side-
2,463,570✔
982
    // effects of deleting the object (i.e. link nullifications). These instructions
2,463,570✔
983
    // must come before the actual deletion of the object, but at the same time
2,463,570✔
984
    // the Replication object may need a consistent view of the row (not including
2,463,570✔
985
    // link columns). Therefore we first nullify links to this object, then
2,463,570✔
986
    // generate the instruction, and then delete the row in the remaining columns.
2,463,570✔
987

2,463,570✔
988
    auto nullify_fwd_links = [&](ColKey col_key) {
4,924,911✔
989
        ColKey::Idx leaf_ndx = col_key.get_index();
187,008✔
990
        auto type = col_key.get_type();
187,008✔
991
        REALM_ASSERT(type == col_type_BackLink);
187,008✔
992
        ArrayBacklink values(m_alloc);
187,008✔
993
        values.set_parent(this, leaf_ndx.val + s_first_col_index);
187,008✔
994
        values.init_from_parent();
187,008✔
995
        // Ensure that Cluster is writable and able to hold references to nodes in
93,411✔
996
        // the slab area before nullifying or deleting links. These operation may
93,411✔
997
        // both have the effect that other objects may be constructed and manipulated.
93,411✔
998
        // If those other object are in the same cluster that the object to be deleted
93,411✔
999
        // is in, then that will cause another accessor to this cluster to be created.
93,411✔
1000
        // It would lead to an error if the cluster node was relocated without it being
93,411✔
1001
        // reflected in the context here.
93,411✔
1002
        values.copy_on_write();
187,008✔
1003
        values.nullify_fwd_links(ndx, state);
187,008✔
1004

93,411✔
1005
        return IteratorControl::AdvanceToNext;
187,008✔
1006
    };
187,008✔
1007

2,463,570✔
1008
    m_tree_top.get_owning_table()->for_each_backlink_column(nullify_fwd_links);
4,924,911✔
1009
}
4,924,911✔
1010

1011
void Cluster::upgrade_string_to_enum(ColKey col_key, ArrayString& keys)
1012
{
1,086✔
1013
    auto col_ndx = col_key.get_index();
1,086✔
1014
    Array indexes(m_alloc);
1,086✔
1015
    indexes.create(Array::type_Normal, false);
1,086✔
1016
    ArrayString values(m_alloc);
1,086✔
1017
    ref_type ref = Array::get_as_ref(col_ndx.val + s_first_col_index);
1,086✔
1018
    values.init_from_ref(ref);
1,086✔
1019
    size_t sz = values.size();
1,086✔
1020
    for (size_t i = 0; i < sz; i++) {
118,542✔
1021
        auto v = values.get(i);
117,456✔
1022
        size_t pos = keys.lower_bound(v);
117,456✔
1023
        REALM_ASSERT_3(pos, !=, keys.size());
117,456✔
1024
        indexes.add(pos);
117,456✔
1025
    }
117,456✔
1026
    Array::set(col_ndx.val + s_first_col_index, indexes.get_ref());
1,086✔
1027
    Array::destroy_deep(ref, m_alloc);
1,086✔
1028
}
1,086✔
1029

1030
void Cluster::init_leaf(ColKey col_key, ArrayPayload* leaf) const
1031
{
7,994,835✔
1032
    auto col_ndx = col_key.get_index();
7,994,835✔
1033
    // FIXME: Move this validation into callers.
3,583,809✔
1034
    // Currently, the query subsystem may call with an unvalidated key.
3,583,809✔
1035
    // once fixed, reintroduce the noexcept declaration :-D
3,583,809✔
1036
    if (auto t = m_tree_top.get_owning_table())
7,994,835✔
1037
        t->check_column(col_key);
8,039,316✔
1038
    ref_type ref = to_ref(Array::get(col_ndx.val + 1));
7,994,835✔
1039
    if (leaf->need_spec()) {
7,994,835✔
1040
        m_tree_top.set_spec(*leaf, col_ndx);
143,391✔
1041
    }
143,391✔
1042
    leaf->init_from_ref(ref);
7,994,835✔
1043
    leaf->set_parent(const_cast<Cluster*>(this), col_ndx.val + 1);
7,994,835✔
1044
}
7,994,835✔
1045

1046
void Cluster::add_leaf(ColKey col_key, ref_type ref)
1047
{
×
1048
    auto col_ndx = col_key.get_index();
×
1049
    REALM_ASSERT((col_ndx.val + 1) == size());
×
1050
    Array::insert(col_ndx.val + 1, from_ref(ref));
×
1051
}
×
1052

1053
template <typename ArrayType>
1054
void Cluster::verify(ref_type ref, size_t index, util::Optional<size_t>& sz) const
1055
{
6,714,954✔
1056
    ArrayType arr(get_alloc());
6,714,954✔
1057
    set_spec(arr, ColKey::Idx{unsigned(index) - 1});
6,714,954✔
1058
    arr.set_parent(const_cast<Cluster*>(this), index);
6,714,954✔
1059
    arr.init_from_ref(ref);
6,714,954✔
1060
    arr.verify();
6,714,954✔
1061
    if (sz) {
6,714,954!
1062
        REALM_ASSERT(arr.size() == *sz);
3,055,500!
1063
    }
3,055,500✔
1064
    else {
3,659,454✔
1065
        sz = arr.size();
3,659,454✔
1066
    }
3,659,454✔
1067
}
6,714,954✔
1068
namespace {
1069

1070
template <typename ArrayType>
1071
void verify_list(ArrayRef& arr, size_t sz)
1072
{
1,594,779✔
1073
    for (size_t n = 0; n < sz; n++) {
6,381,771!
1074
        if (ref_type bp_tree_ref = arr.get(n)) {
4,786,992!
1075
            BPlusTree<ArrayType> links(arr.get_alloc());
1,689,513✔
1076
            links.init_from_ref(bp_tree_ref);
1,689,513✔
1077
            links.set_parent(&arr, n);
1,689,513✔
1078
            links.verify();
1,689,513✔
1079
        }
1,689,513✔
1080
    }
4,786,992✔
1081
}
1,594,779✔
1082

1083
template <typename SetType>
1084
void verify_set(ArrayRef& arr, size_t sz)
1085
{
120✔
1086
    for (size_t n = 0; n < sz; ++n) {
282!
1087
        if (ref_type bp_tree_ref = arr.get(n)) {
162!
1088
            BPlusTree<SetType> elements(arr.get_alloc());
162✔
1089
            elements.init_from_ref(bp_tree_ref);
162✔
1090
            elements.set_parent(&arr, n);
162✔
1091
            elements.verify();
162✔
1092

81✔
1093
            // FIXME: Check uniqueness of elements.
81✔
1094
        }
162✔
1095
    }
162✔
1096
}
120✔
1097

1098
} // namespace
1099

1100
void Cluster::verify() const
1101
{
3,696,069✔
1102
#ifdef REALM_DEBUG
3,696,069✔
1103
    util::Optional<size_t> sz;
3,696,069✔
1104

1,883,085✔
1105
    auto verify_column = [this, &sz](ColKey col_key) {
8,322,123✔
1106
        size_t col = col_key.get_index().val + s_first_col_index;
8,322,123✔
1107
        ref_type ref = Array::get_as_ref(col);
8,322,123✔
1108
        auto attr = col_key.get_attrs();
8,322,123✔
1109
        auto col_type = col_key.get_type();
8,322,123✔
1110
        bool nullable = attr.test(col_attr_Nullable);
8,322,123✔
1111

4,212,720✔
1112
        if (attr.test(col_attr_List)) {
8,322,123✔
1113
            ArrayRef arr(get_alloc());
1,594,794✔
1114
            arr.set_parent(const_cast<Cluster*>(this), col);
1,594,794✔
1115
            arr.init_from_ref(ref);
1,594,794✔
1116
            arr.verify();
1,594,794✔
1117
            if (sz) {
1,594,794✔
1118
                REALM_ASSERT(arr.size() == *sz);
1,570,512✔
1119
            }
1,570,512✔
1120
            else {
24,282✔
1121
                sz = arr.size();
24,282✔
1122
            }
24,282✔
1123

825,108✔
1124
            switch (col_type) {
1,594,794✔
1125
                case col_type_Int:
808,875✔
1126
                    if (nullable) {
808,875✔
1127
                        verify_list<util::Optional<int64_t>>(arr, *sz);
24✔
1128
                    }
24✔
1129
                    else {
808,851✔
1130
                        verify_list<int64_t>(arr, *sz);
808,851✔
1131
                    }
808,851✔
1132
                    break;
808,875✔
1133
                case col_type_Bool:
✔
1134
                    verify_list<Bool>(arr, *sz);
×
1135
                    break;
×
1136
                case col_type_Float:
6✔
1137
                    verify_list<Float>(arr, *sz);
6✔
1138
                    break;
6✔
1139
                case col_type_Double:
✔
1140
                    verify_list<Double>(arr, *sz);
×
1141
                    break;
×
1142
                case col_type_String:
760,701✔
1143
                    verify_list<String>(arr, *sz);
760,701✔
1144
                    break;
760,701✔
1145
                case col_type_Binary:
24,000✔
1146
                    verify_list<Binary>(arr, *sz);
24,000✔
1147
                    break;
24,000✔
1148
                case col_type_Timestamp:
✔
1149
                    verify_list<Timestamp>(arr, *sz);
×
1150
                    break;
×
1151
                case col_type_Decimal:
✔
1152
                    verify_list<Decimal128>(arr, *sz);
×
1153
                    break;
×
1154
                case col_type_ObjectId:
✔
1155
                    verify_list<ObjectId>(arr, *sz);
×
1156
                    break;
×
1157
                case col_type_UUID:
✔
1158
                    verify_list<UUID>(arr, *sz);
×
1159
                    break;
×
1160
                case col_type_LinkList:
1,197✔
1161
                    verify_list<ObjKey>(arr, *sz);
1,197✔
1162
                    break;
1,197✔
1163
                default:
18✔
1164
                    // FIXME: Nullable primitives
9✔
1165
                    break;
18✔
1166
            }
1,594,797✔
1167
            return IteratorControl::AdvanceToNext;
1,594,797✔
1168
        }
1,594,797✔
1169
        else if (attr.test(col_attr_Dictionary)) {
6,727,329✔
1170
            ArrayRef arr(get_alloc());
12,156✔
1171
            arr.set_parent(const_cast<Cluster*>(this), col);
12,156✔
1172
            arr.init_from_ref(ref);
12,156✔
1173
            arr.verify();
12,156✔
1174
            if (sz) {
12,156✔
1175
                REALM_ASSERT(arr.size() == *sz);
126✔
1176
            }
126✔
1177
            else {
12,030✔
1178
                sz = arr.size();
12,030✔
1179
            }
12,030✔
1180
            for (size_t n = 0; n < sz; n++) {
24,414✔
1181
                if (auto ref = arr.get(n)) {
12,258✔
1182
                    Dictionary dict(get_alloc(), col_key, to_ref(ref));
12,165✔
1183
                    dict.verify();
12,165✔
1184
                }
12,165✔
1185
            }
12,258✔
1186
            return IteratorControl::AdvanceToNext;
12,156✔
1187
        }
12,156✔
1188
        else if (attr.test(col_attr_Set)) {
6,715,173✔
1189
            ArrayRef arr(get_alloc());
216✔
1190
            arr.set_parent(const_cast<Cluster*>(this), col);
216✔
1191
            arr.init_from_ref(ref);
216✔
1192
            arr.verify();
216✔
1193
            if (sz) {
216✔
1194
                REALM_ASSERT(arr.size() == *sz);
198✔
1195
            }
198✔
1196
            else {
18✔
1197
                sz = arr.size();
18✔
1198
            }
18✔
1199
            switch (col_type) {
216✔
1200
                case col_type_Int:
96✔
1201
                    if (nullable) {
96✔
1202
                        verify_set<util::Optional<int64_t>>(arr, *sz);
×
1203
                    }
×
1204
                    else {
96✔
1205
                        verify_set<int64_t>(arr, *sz);
96✔
1206
                    }
96✔
1207
                    break;
96✔
1208
                case col_type_Bool:
✔
1209
                    verify_set<Bool>(arr, *sz);
×
1210
                    break;
×
1211
                case col_type_Float:
✔
1212
                    verify_set<Float>(arr, *sz);
×
1213
                    break;
×
1214
                case col_type_Double:
✔
1215
                    verify_set<Double>(arr, *sz);
×
1216
                    break;
×
1217
                case col_type_String:
✔
1218
                    verify_set<String>(arr, *sz);
×
1219
                    break;
×
1220
                case col_type_Binary:
12✔
1221
                    verify_set<Binary>(arr, *sz);
12✔
1222
                    break;
12✔
1223
                case col_type_Timestamp:
✔
1224
                    verify_set<Timestamp>(arr, *sz);
×
1225
                    break;
×
1226
                case col_type_Decimal:
✔
1227
                    verify_set<Decimal128>(arr, *sz);
×
1228
                    break;
×
1229
                case col_type_ObjectId:
✔
1230
                    verify_set<ObjectId>(arr, *sz);
×
1231
                    break;
×
1232
                case col_type_UUID:
✔
1233
                    verify_set<UUID>(arr, *sz);
×
1234
                    break;
×
1235
                case col_type_Link:
12✔
1236
                    verify_set<ObjKey>(arr, *sz);
12✔
1237
                    break;
12✔
1238
                default:
96✔
1239
                    // FIXME: Nullable primitives
48✔
1240
                    break;
96✔
1241
            }
216✔
1242
            return IteratorControl::AdvanceToNext;
216✔
1243
        }
216✔
1244

3,381,426✔
1245
        switch (col_type) {
6,714,957✔
1246
            case col_type_Int:
4,521,594✔
1247
                if (nullable) {
4,521,594✔
1248
                    verify<ArrayIntNull>(ref, col, sz);
750,204✔
1249
                }
750,204✔
1250
                else {
3,771,390✔
1251
                    verify<ArrayInteger>(ref, col, sz);
3,771,390✔
1252
                }
3,771,390✔
1253
                break;
4,521,594✔
1254
            case col_type_Bool:
7,224✔
1255
                verify<ArrayBoolNull>(ref, col, sz);
7,224✔
1256
                break;
7,224✔
1257
            case col_type_Float:
174✔
1258
                verify<ArrayFloatNull>(ref, col, sz);
174✔
1259
                break;
174✔
1260
            case col_type_Double:
186✔
1261
                verify<ArrayDoubleNull>(ref, col, sz);
186✔
1262
                break;
186✔
1263
            case col_type_String:
2,174,748✔
1264
                verify<ArrayString>(ref, col, sz);
2,174,748✔
1265
                break;
2,174,748✔
1266
            case col_type_Binary:
174✔
1267
                verify<ArrayBinary>(ref, col, sz);
174✔
1268
                break;
174✔
1269
            case col_type_Mixed:
6✔
1270
                verify<ArrayMixed>(ref, col, sz);
6✔
1271
                break;
6✔
1272
            case col_type_Timestamp:
6,984✔
1273
                verify<ArrayTimestamp>(ref, col, sz);
6,984✔
1274
                break;
6,984✔
1275
            case col_type_Decimal:
42✔
1276
                verify<ArrayDecimal128>(ref, col, sz);
42✔
1277
                break;
42✔
1278
            case col_type_ObjectId:
42✔
1279
                verify<ArrayObjectIdNull>(ref, col, sz);
42✔
1280
                break;
42✔
1281
            case col_type_UUID:
✔
1282
                verify<ArrayUUIDNull>(ref, col, sz);
×
1283
                break;
×
1284
            case col_type_Link:
1,137✔
1285
                verify<ArrayKey>(ref, col, sz);
1,137✔
1286
                break;
1,137✔
1287
            case col_type_BackLink:
2,640✔
1288
                verify<ArrayBacklink>(ref, col, sz);
2,640✔
1289
                break;
2,640✔
1290
            default:
6✔
1291
                break;
6✔
1292
        }
6,714,969✔
1293
        return IteratorControl::AdvanceToNext;
6,714,969✔
1294
    };
6,714,969✔
1295

1,883,085✔
1296
    m_tree_top.m_owner->for_each_and_every_column(verify_column);
3,696,069✔
1297
#endif
3,696,069✔
1298
}
3,696,069✔
1299

1300
// LCOV_EXCL_START
1301
void Cluster::dump_objects(int64_t key_offset, std::string lead) const
1302
{
×
1303
    std::cout << lead << "leaf - size: " << node_size() << std::endl;
×
1304
    if (!m_keys.is_attached()) {
×
1305
        std::cout << lead << "compact form" << std::endl;
×
1306
    }
×
1307

1308
    for (unsigned i = 0; i < node_size(); i++) {
×
1309
        int64_t key_value;
×
1310
        if (m_keys.is_attached()) {
×
1311
            key_value = m_keys.get(i);
×
1312
        }
×
1313
        else {
×
1314
            key_value = int64_t(i);
×
1315
        }
×
1316
        std::cout << lead << "key: " << std::hex << key_value + key_offset << std::dec;
×
1317
        m_tree_top.m_owner->for_each_and_every_column([&](ColKey col) {
×
1318
            size_t j = col.get_index().val + 1;
×
1319
            if (col.get_attrs().test(col_attr_List)) {
×
1320
                ref_type ref = Array::get_as_ref(j);
×
1321
                ArrayRef refs(m_alloc);
×
1322
                refs.init_from_ref(ref);
×
1323
                std::cout << ", {";
×
1324
                ref = refs.get(i);
×
1325
                if (ref) {
×
1326
                    if (col.get_type() == col_type_Int) {
×
1327
                        // This is easy to handle
1328
                        Array ints(m_alloc);
×
1329
                        ints.init_from_ref(ref);
×
1330
                        for (size_t n = 0; n < ints.size(); n++) {
×
1331
                            std::cout << ints.get(n) << ", ";
×
1332
                        }
×
1333
                    }
×
1334
                    else {
×
1335
                        std::cout << col.get_type();
×
1336
                    }
×
1337
                }
×
1338
                std::cout << "}";
×
1339
                return IteratorControl::AdvanceToNext;
×
1340
            }
×
1341

1342
            switch (col.get_type()) {
×
1343
                case col_type_Int: {
×
1344
                    bool nullable = col.get_attrs().test(col_attr_Nullable);
×
1345
                    ref_type ref = Array::get_as_ref(j);
×
1346
                    if (nullable) {
×
1347
                        ArrayIntNull arr_int_null(m_alloc);
×
1348
                        arr_int_null.init_from_ref(ref);
×
1349
                        if (arr_int_null.is_null(i)) {
×
1350
                            std::cout << ", null";
×
1351
                        }
×
1352
                        else {
×
1353
                            std::cout << ", " << *arr_int_null.get(i);
×
1354
                        }
×
1355
                    }
×
1356
                    else {
×
1357
                        Array arr(m_alloc);
×
1358
                        arr.init_from_ref(ref);
×
1359
                        std::cout << ", " << arr.get(i);
×
1360
                    }
×
1361
                    break;
×
1362
                }
×
1363
                case col_type_Bool: {
×
1364
                    ArrayBoolNull arr(m_alloc);
×
1365
                    ref_type ref = Array::get_as_ref(j);
×
1366
                    arr.init_from_ref(ref);
×
1367
                    auto val = arr.get(i);
×
1368
                    std::cout << ", " << (val ? (*val ? "true" : "false") : "null");
×
1369
                    break;
×
1370
                }
×
1371
                case col_type_Float: {
×
1372
                    ArrayFloatNull arr(m_alloc);
×
1373
                    ref_type ref = Array::get_as_ref(j);
×
1374
                    arr.init_from_ref(ref);
×
1375
                    auto val = arr.get(i);
×
1376
                    if (val)
×
1377
                        std::cout << ", " << *val;
×
1378
                    else
×
1379
                        std::cout << ", null";
×
1380
                    break;
×
1381
                }
×
1382
                case col_type_Double: {
×
1383
                    ArrayDoubleNull arr(m_alloc);
×
1384
                    ref_type ref = Array::get_as_ref(j);
×
1385
                    arr.init_from_ref(ref);
×
1386
                    auto val = arr.get(i);
×
1387
                    if (val)
×
1388
                        std::cout << ", " << *val;
×
1389
                    else
×
1390
                        std::cout << ", null";
×
1391
                    break;
×
1392
                }
×
1393
                case col_type_String: {
×
1394
                    ArrayString arr(m_alloc);
×
1395
                    ref_type ref = Array::get_as_ref(j);
×
1396
                    arr.init_from_ref(ref);
×
1397
                    std::cout << ", " << arr.get(i);
×
1398
                    break;
×
1399
                }
×
1400
                case col_type_Binary: {
×
1401
                    ArrayBinary arr(m_alloc);
×
1402
                    ref_type ref = Array::get_as_ref(j);
×
1403
                    arr.init_from_ref(ref);
×
1404
                    std::cout << ", " << arr.get(i);
×
1405
                    break;
×
1406
                }
×
1407
                case col_type_Mixed: {
×
1408
                    ArrayMixed arr(m_alloc);
×
1409
                    ref_type ref = Array::get_as_ref(j);
×
1410
                    arr.init_from_ref(ref);
×
1411
                    std::cout << ", " << arr.get(i);
×
1412
                    break;
×
1413
                }
×
1414
                case col_type_Timestamp: {
×
1415
                    ArrayTimestamp arr(m_alloc);
×
1416
                    ref_type ref = Array::get_as_ref(j);
×
1417
                    arr.init_from_ref(ref);
×
1418
                    if (arr.is_null(i)) {
×
1419
                        std::cout << ", null";
×
1420
                    }
×
1421
                    else {
×
1422
                        std::cout << ", " << arr.get(i);
×
1423
                    }
×
1424
                    break;
×
1425
                }
×
1426
                case col_type_Decimal: {
×
1427
                    ArrayDecimal128 arr(m_alloc);
×
1428
                    ref_type ref = Array::get_as_ref(j);
×
1429
                    arr.init_from_ref(ref);
×
1430
                    if (arr.is_null(i)) {
×
1431
                        std::cout << ", null";
×
1432
                    }
×
1433
                    else {
×
1434
                        std::cout << ", " << arr.get(i);
×
1435
                    }
×
1436
                    break;
×
1437
                }
×
1438
                case col_type_ObjectId: {
×
1439
                    ArrayObjectIdNull arr(m_alloc);
×
1440
                    ref_type ref = Array::get_as_ref(j);
×
1441
                    arr.init_from_ref(ref);
×
1442
                    if (arr.is_null(i)) {
×
1443
                        std::cout << ", null";
×
1444
                    }
×
1445
                    else {
×
1446
                        std::cout << ", " << *arr.get(i);
×
1447
                    }
×
1448
                    break;
×
1449
                }
×
1450
                case col_type_UUID: {
×
1451
                    ArrayUUIDNull arr(m_alloc);
×
1452
                    ref_type ref = Array::get_as_ref(j);
×
1453
                    arr.init_from_ref(ref);
×
1454
                    if (arr.is_null(i)) {
×
1455
                        std::cout << ", null";
×
1456
                    }
×
1457
                    else {
×
1458
                        std::cout << ", " << arr.get(i);
×
1459
                    }
×
1460
                    break;
×
1461
                }
×
1462
                case col_type_Link: {
×
1463
                    ArrayKey arr(m_alloc);
×
1464
                    ref_type ref = Array::get_as_ref(j);
×
1465
                    arr.init_from_ref(ref);
×
1466
                    std::cout << ", " << arr.get(i);
×
1467
                    break;
×
1468
                }
×
1469
                case col_type_BackLink: {
×
1470
                    break;
×
1471
                }
×
1472
                default:
×
1473
                    std::cout << ", Error";
×
1474
                    break;
×
1475
            }
×
1476
            return IteratorControl::AdvanceToNext;
×
1477
        });
×
1478
        std::cout << std::endl;
×
1479
    }
×
1480
}
×
1481
// LCOV_EXCL_STOP
1482

1483
void Cluster::remove_backlinks(ObjKey origin_key, ColKey origin_col_key, const std::vector<ObjKey>& keys,
1484
                               CascadeState& state) const
1485
{
6,654✔
1486
    const Table* origin_table = m_tree_top.get_owning_table();
6,654✔
1487
    TableRef target_table = origin_table->get_opposite_table(origin_col_key);
6,654✔
1488
    ColKey backlink_col_key = origin_table->get_opposite_column(origin_col_key);
6,654✔
1489
    bool strong_links = target_table->is_embedded();
6,654✔
1490

3,324✔
1491
    for (auto key : keys) {
8,214✔
1492
        if (key != null_key) {
8,214✔
1493
            bool is_unres = key.is_unresolved();
8,214✔
1494
            Obj target_obj = is_unres ? target_table->m_tombstones->get(key) : target_table->m_clusters.get(key);
8,199✔
1495
            bool last_removed = target_obj.remove_one_backlink(backlink_col_key, origin_key); // Throws
8,214✔
1496
            if (is_unres) {
8,214✔
1497
                if (last_removed) {
30✔
1498
                    // Check is there are more backlinks
12✔
1499
                    if (!target_obj.has_backlinks(false)) {
24✔
1500
                        // Tombstones can be erased right away - there is no cascading effect
9✔
1501
                        target_table->m_tombstones->erase(key, state);
18✔
1502
                    }
18✔
1503
                }
24✔
1504
            }
30✔
1505
            else {
8,184✔
1506
                state.enqueue_for_cascade(target_obj, strong_links, last_removed);
8,184✔
1507
            }
8,184✔
1508
        }
8,214✔
1509
    }
8,214✔
1510
}
6,654✔
1511

1512
void Cluster::remove_backlinks(ObjKey origin_key, ColKey origin_col_key, const std::vector<ObjLink>& links,
1513
                               CascadeState& state) const
1514
{
162✔
1515
    const Table* origin_table = m_tree_top.get_owning_table();
162✔
1516
    Group* group = origin_table->get_parent_group();
162✔
1517
    TableKey origin_table_key = origin_table->get_key();
162✔
1518

81✔
1519
    for (auto link : links) {
270✔
1520
        if (link) {
270✔
1521
            bool is_unres = link.get_obj_key().is_unresolved();
270✔
1522
            Obj target_obj = group->get_object(link);
270✔
1523
            TableRef target_table = target_obj.get_table();
270✔
1524
            ColKey backlink_col_key = target_table->find_or_add_backlink_column(origin_col_key, origin_table_key);
270✔
1525

135✔
1526
            bool last_removed = target_obj.remove_one_backlink(backlink_col_key, origin_key); // Throws
270✔
1527
            if (is_unres) {
270✔
1528
                if (last_removed) {
12✔
1529
                    // Check is there are more backlinks
6✔
1530
                    if (!target_obj.has_backlinks(false)) {
12✔
1531
                        // Tombstones can be erased right away - there is no cascading effect
3✔
1532
                        target_table->m_tombstones->erase(link.get_obj_key(), state);
6✔
1533
                    }
6✔
1534
                }
12✔
1535
            }
12✔
1536
            else {
258✔
1537
                state.enqueue_for_cascade(target_obj, false, last_removed);
258✔
1538
            }
258✔
1539
        }
270✔
1540
    }
270✔
1541
}
162✔
1542

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