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

realm / realm-core / github_pull_request_301264

30 Jul 2024 07:11PM UTC coverage: 91.111% (+0.009%) from 91.102%
github_pull_request_301264

Pull #7936

Evergreen

web-flow
Add support for multi-process subscription state change notifications (#7862)

As with the other multi-process notifications, the core idea here is to
eliminate the in-memory state and produce notifications based entirely on the
current state of the Realm file.

SubscriptionStore::update_state() has been replaced with separate functions for
the specific legal state transitions, which also take a write transaction as a
parameter. These functions are called by PendingBootstrapStore inside the same
write transaction as the bootstrap updates which changed the subscription
state. This is both a minor performance optimization (due to fewer writes) and
eliminates a brief window between the two writes where the Realm file was in an
inconsistent state.

There's a minor functional change here: previously old subscription sets were
superseded when the new one reached the Completed state, and now they are
superseded on AwaitingMark. This aligns it with when the new subscription set
becomes the one which is returned by get_active().
Pull Request #7936: Fix connection callback crashes when reloading with React Native

102800 of 181570 branches covered (56.62%)

216840 of 237996 relevant lines covered (91.11%)

5918493.47 hits per line

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

75.85
/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 "realm/list.hpp"
37
#include <iostream>
38
#include <cmath>
39

40
namespace realm {
41

42
/******************************* FieldValues *********************************/
43

44
FieldValues::FieldValues(std::initializer_list<FieldValue> init)
45
    : m_values(init)
542,337✔
46
{
1,106,286✔
47
    if (m_values.size() > 1) {
1,106,286✔
48
        // Sort according to ColKey index
49
        std::sort(m_values.begin(), m_values.end(), [](const auto& a, const auto& b) {
53,679✔
50
            return a.col_key.get_index().val < b.col_key.get_index().val;
53,679✔
51
        });
53,679✔
52
    }
17,928✔
53
}
1,106,286✔
54

55
void FieldValues::insert(ColKey k, Mixed val, bool is_default)
56
{
905,604✔
57
    if (m_values.empty()) {
905,604✔
58
        m_values.emplace_back(k, val, is_default);
893,028✔
59
        return;
893,028✔
60
    }
893,028✔
61
    unsigned int idx = k.get_index().val;
12,576✔
62
    auto it = std::lower_bound(m_values.begin(), m_values.end(), idx, [](const auto& a, unsigned int i) {
24,474✔
63
        return a.col_key.get_index().val < i;
24,474✔
64
    });
24,474✔
65
    m_values.insert(it, {k, val, is_default});
12,576✔
66
}
12,576✔
67

68
/******************************* ClusterNode *********************************/
69

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

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

85
const Table* ClusterNode::get_owning_table() const noexcept
86
{
129,069✔
87
    return m_tree_top.get_owning_table();
129,069✔
88
}
129,069✔
89

90
void ClusterNode::get(ObjKey k, ClusterNode::State& state) const
91
{
105,307,938✔
92
    if (!k || !try_get(RowKey(k), state)) {
105,307,938✔
93
        throw KeyNotFound(util::format("No object with key '%1' in '%2'", k.value, get_owning_table()->get_name()));
210✔
94
    }
210✔
95
}
105,307,938✔
96

97

98
/********************************* Cluster ***********************************/
99

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

105
    arr.add(RefOrTagged::make_tagged(0)); // Compact form
288,330✔
106
    return arr.get_mem();
288,330✔
107
}
288,330✔
108

109

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

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

125
    auto column_initialize = [this](ColKey col_key) {
193,419✔
126
        auto col_ndx = col_key.get_index();
193,419✔
127
        while (size() <= col_ndx.val + 1)
386,844✔
128
            add(0);
193,425✔
129
        auto type = col_key.get_type();
193,419✔
130
        auto attr = col_key.get_attrs();
193,419✔
131
        if (attr.test(col_attr_Collection)) {
193,419✔
132
            ArrayRef arr(m_alloc);
6,783✔
133
            arr.create();
6,783✔
134
            arr.set_parent(this, col_ndx.val + s_first_col_index);
6,783✔
135
            arr.update_parent();
6,783✔
136
            return IteratorControl::AdvanceToNext;
6,783✔
137
        }
6,783✔
138
        switch (type) {
186,636✔
139
            case col_type_Int:
112,632✔
140
                if (attr.test(col_attr_Nullable)) {
112,632✔
141
                    do_create<ArrayIntNull>(col_key);
10,479✔
142
                }
10,479✔
143
                else {
102,153✔
144
                    do_create<ArrayInteger>(col_key);
102,153✔
145
                }
102,153✔
146
                break;
112,632✔
147
            case col_type_Bool:
684✔
148
                do_create<ArrayBoolNull>(col_key);
684✔
149
                break;
684✔
150
            case col_type_Float:
1,104✔
151
                do_create<ArrayFloatNull>(col_key);
1,104✔
152
                break;
1,104✔
153
            case col_type_Double:
5,790✔
154
                do_create<ArrayDoubleNull>(col_key);
5,790✔
155
                break;
5,790✔
156
            case col_type_String: {
48,393✔
157
                if (m_tree_top.is_string_enum_type(col_ndx)) {
48,393✔
158
                    do_create<ArrayInteger>(col_key);
5,340✔
159
                }
5,340✔
160
                else {
43,053✔
161
                    do_create<ArrayString>(col_key);
43,053✔
162
                }
43,053✔
163
                break;
48,393✔
164
            }
×
165
            case col_type_Binary:
5,280✔
166
                do_create<ArrayBinary>(col_key);
5,280✔
167
                break;
5,280✔
168
            case col_type_Mixed:
636✔
169
                do_create<ArrayMixed>(col_key);
636✔
170
                break;
636✔
171
            case col_type_Timestamp:
2,514✔
172
                do_create<ArrayTimestamp>(col_key);
2,514✔
173
                break;
2,514✔
174
            case col_type_Decimal:
276✔
175
                do_create<ArrayDecimal128>(col_key);
276✔
176
                break;
276✔
177
            case col_type_ObjectId:
2,934✔
178
                do_create<ArrayObjectIdNull>(col_key);
2,934✔
179
                break;
2,934✔
180
            case col_type_UUID:
408✔
181
                do_create<ArrayUUIDNull>(col_key);
408✔
182
                break;
408✔
183
            case col_type_Link:
2,280✔
184
                do_create<ArrayKey>(col_key);
2,280✔
185
                break;
2,280✔
186
            case col_type_TypedLink:
✔
187
                do_create<ArrayTypedLink>(col_key);
×
188
                break;
×
189
            case col_type_BackLink:
3,705✔
190
                do_create<ArrayBacklink>(col_key);
3,705✔
191
                break;
3,705✔
192
            default:
✔
193
                REALM_UNREACHABLE();
194
        }
186,636✔
195
        return IteratorControl::AdvanceToNext;
186,633✔
196
    };
186,636✔
197
    m_tree_top.m_owner->for_each_and_every_column(column_initialize);
97,869✔
198

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

207
void Cluster::init(MemRef mem)
208
{
216,419,871✔
209
    Array::init_from_mem(mem);
216,419,871✔
210
    auto rot = Array::get_as_ref_or_tagged(0);
216,419,871✔
211
    if (rot.is_tagged()) {
216,419,871✔
212
        m_keys.detach();
153,405,204✔
213
    }
153,405,204✔
214
    else {
63,014,667✔
215
        m_keys.init_from_ref(rot.get_as_ref());
63,014,667✔
216
    }
63,014,667✔
217
}
216,419,871✔
218

219
void Cluster::update_from_parent() noexcept
220
{
674,670✔
221
    Array::update_from_parent();
674,670✔
222
    auto rot = Array::get_as_ref_or_tagged(0);
674,670✔
223
    if (!rot.is_tagged()) {
674,670✔
224
        m_keys.update_from_parent();
29,361✔
225
    }
29,361✔
226
}
674,670✔
227

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

234
    return get_mem();
×
235
}
×
236

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

242
size_t Cluster::node_size_from_header(Allocator& alloc, const char* header)
243
{
71,853,399✔
244
    auto rot = Array::get_as_ref_or_tagged(header, s_key_ref_or_size_index);
71,853,399✔
245
    if (rot.is_tagged()) {
71,853,399✔
246
        return size_t(rot.get_as_int());
70,417,839✔
247
    }
70,417,839✔
248
    else {
1,435,560✔
249
        return Array::get_size_from_header(alloc.translate(rot.get_as_ref()));
1,435,560✔
250
    }
1,435,560✔
251
}
71,853,399✔
252

253
template <class T>
254
inline void Cluster::set_spec(T&, ColKey::Idx) const
255
{
37,423,656✔
256
}
37,423,656✔
257

258
template <>
259
inline void Cluster::set_spec(ArrayString& arr, ColKey::Idx col_ndx) const
260
{
3,732,111✔
261
    m_tree_top.set_spec(arr, col_ndx);
3,732,111✔
262
}
3,732,111✔
263

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

269
    T arr(m_alloc);
33,508,647✔
270
    auto col_ndx = col.get_index();
33,508,647✔
271
    arr.set_parent(this, col_ndx.val + s_first_col_index);
33,508,647✔
272
    set_spec<T>(arr, col_ndx);
33,508,647✔
273
    arr.init_from_parent();
33,508,647✔
274
    if (init_val.is_null()) {
33,508,647✔
275
        arr.insert(ndx, T::default_value(nullable));
32,407,977✔
276
    }
32,407,977✔
277
    else {
1,100,670✔
278
        arr.insert(ndx, init_val.get<U>());
1,100,670✔
279
    }
1,100,670✔
280
}
33,508,647✔
281

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

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

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

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

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

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

341
        ColKey backlink_col_key = target_table->find_or_add_backlink_column(col_key, origin_table->get_key());
×
342
        target_table->get_object(target_link.get_obj_key()).add_backlink(backlink_col_key, origin_key);
×
343
    }
×
344
}
×
345

346
void Cluster::insert_row(size_t ndx, RowKey row_key, const FieldValues& init_values)
347
{
23,834,838✔
348
    // Ensure the cluster array is big enough to hold 64 bit values.
349
    copy_on_write(m_size * 8);
23,834,838✔
350

351
    if (m_keys.is_attached()) {
23,834,838✔
352
        m_keys.insert(ndx, row_key.value);
1,628,955✔
353
    }
1,628,955✔
354
    else {
22,205,883✔
355
        Array::set(s_key_ref_or_size_index, Array::get(s_key_ref_or_size_index) + 2); // Increments size by 1
22,205,883✔
356
    }
22,205,883✔
357

358
    auto val = init_values.begin();
23,834,838✔
359
    auto insert_in_column = [&](ColKey col_key) {
35,796,231✔
360
        auto col_ndx = col_key.get_index();
35,796,231✔
361
        auto attr = col_key.get_attrs();
35,796,231✔
362
        Mixed init_value;
35,796,231✔
363
        // init_values must be sorted in col_ndx order - this is ensured by ClustTree::insert()
364
        if (val != init_values.end() && val->col_key.get_index().val == col_ndx.val) {
35,796,231✔
365
            init_value = val->value;
1,000,878✔
366
            ++val;
1,000,878✔
367
        }
1,000,878✔
368

369
        auto type = col_key.get_type();
35,796,231✔
370
        if (attr.test(col_attr_Collection)) {
35,796,231✔
371
            REALM_ASSERT(init_value.is_null());
753,312✔
372
            ArrayRef arr(m_alloc);
753,312✔
373
            arr.set_parent(this, col_ndx.val + s_first_col_index);
753,312✔
374
            arr.init_from_parent();
753,312✔
375
            arr.insert(ndx, 0);
753,312✔
376
            return IteratorControl::AdvanceToNext;
753,312✔
377
        }
753,312✔
378

379
        bool nullable = attr.test(col_attr_Nullable);
35,042,919✔
380
        ObjKey obj_key(int64_t(row_key.value + get_offset()));
35,042,919✔
381
        switch (type) {
35,042,919✔
382
            case col_type_Int:
26,298,438✔
383
                if (attr.test(col_attr_Nullable)) {
26,298,438✔
384
                    do_insert_row<ArrayIntNull>(ndx, col_key, init_value, nullable);
2,037,471✔
385
                }
2,037,471✔
386
                else {
24,260,967✔
387
                    do_insert_row<ArrayInteger>(ndx, col_key, init_value, nullable);
24,260,967✔
388
                }
24,260,967✔
389
                break;
26,298,438✔
390
            case col_type_Bool:
187,269✔
391
                do_insert_row<ArrayBoolNull>(ndx, col_key, init_value, nullable);
187,269✔
392
                break;
187,269✔
393
            case col_type_Float:
323,688✔
394
                do_insert_row<ArrayFloatNull>(ndx, col_key, init_value, nullable);
323,688✔
395
                break;
323,688✔
396
            case col_type_Double:
1,493,328✔
397
                do_insert_row<ArrayDoubleNull>(ndx, col_key, init_value, nullable);
1,493,328✔
398
                break;
1,493,328✔
399
            case col_type_String:
3,161,664✔
400
                do_insert_row<ArrayString>(ndx, col_key, init_value, nullable);
3,161,664✔
401
                break;
3,161,664✔
402
            case col_type_Binary:
1,366,269✔
403
                do_insert_row<ArrayBinary>(ndx, col_key, init_value, nullable);
1,366,269✔
404
                break;
1,366,269✔
405
            case col_type_Mixed: {
166,338✔
406
                do_insert_mixed(ndx, col_key, init_value, obj_key);
166,338✔
407
                break;
166,338✔
408
            }
×
409
            case col_type_Timestamp:
227,157✔
410
                do_insert_row<ArrayTimestamp>(ndx, col_key, init_value, nullable);
227,157✔
411
                break;
227,157✔
412
            case col_type_Decimal:
78,222✔
413
                do_insert_row<ArrayDecimal128>(ndx, col_key, init_value, nullable);
78,222✔
414
                break;
78,222✔
415
            case col_type_ObjectId:
240,252✔
416
                do_insert_row<ArrayObjectIdNull>(ndx, col_key, init_value, nullable);
240,252✔
417
                break;
240,252✔
418
            case col_type_UUID:
119,652✔
419
                do_insert_row<ArrayUUIDNull>(ndx, col_key, init_value, nullable);
119,652✔
420
                break;
119,652✔
421
            case col_type_Link:
285,408✔
422
                do_insert_key(ndx, col_key, init_value, obj_key);
285,408✔
423
                break;
285,408✔
424
            case col_type_TypedLink:
✔
425
                do_insert_link(ndx, col_key, init_value, obj_key);
×
426
                break;
×
427
            case col_type_BackLink: {
1,153,986✔
428
                ArrayBacklink arr(m_alloc);
1,153,986✔
429
                arr.set_parent(this, col_ndx.val + s_first_col_index);
1,153,986✔
430
                arr.init_from_parent();
1,153,986✔
431
                arr.insert(ndx, 0);
1,153,986✔
432
                break;
1,153,986✔
433
            }
×
434
            default:
✔
435
                REALM_ASSERT(false);
×
436
                break;
×
437
        }
35,042,919✔
438
        return IteratorControl::AdvanceToNext;
34,903,488✔
439
    };
35,042,919✔
440
    m_tree_top.m_owner->for_each_and_every_column(insert_in_column);
23,834,838✔
441
}
23,834,838✔
442

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

451
    T dst(m_alloc);
24,099✔
452
    dst.set_parent(to, col_ndx);
24,099✔
453
    dst.init_from_parent();
24,099✔
454

455
    src.move(dst, ndx);
24,099✔
456
}
24,099✔
457

458
void Cluster::move(size_t ndx, ClusterNode* new_node, int64_t offset)
459
{
12,615✔
460
    auto new_leaf = static_cast<Cluster*>(new_node);
12,615✔
461

462
    auto move_from_column = [&](ColKey col_key) {
24,099✔
463
        auto attr = col_key.get_attrs();
24,099✔
464
        auto type = col_key.get_type();
24,099✔
465

466
        if (attr.test(col_attr_Collection)) {
24,099✔
467
            do_move<ArrayRef>(ndx, col_key, new_leaf);
294✔
468
            return IteratorControl::AdvanceToNext;
294✔
469
        }
294✔
470

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

536
Cluster::~Cluster() {}
221,966,562✔
537

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

546
void Cluster::ensure_general_form()
547
{
66,435✔
548
    if (!m_keys.is_attached()) {
66,435✔
549
        size_t current_size = get_size_in_compact_form();
51,036✔
550
        m_keys.create(current_size, 255);
51,036✔
551
        m_keys.update_parent();
51,036✔
552
        for (size_t i = 0; i < current_size; i++) {
5,812,152✔
553
            m_keys.set(i, i);
5,761,116✔
554
        }
5,761,116✔
555
    }
51,036✔
556
}
66,435✔
557

558
template <class T>
559
inline void Cluster::do_insert_column(ColKey col_key, bool nullable)
560
{
623,991✔
561
    size_t sz = node_size();
623,991✔
562

563
    T arr(m_alloc);
623,991✔
564
    arr.create();
623,991✔
565
    auto val = T::default_value(nullable);
623,991✔
566
    for (size_t i = 0; i < sz; i++) {
6,866,682✔
567
        arr.add(val);
6,242,691✔
568
    }
6,242,691✔
569
    auto col_ndx = col_key.get_index();
623,991✔
570
    unsigned ndx = col_ndx.val + s_first_col_index;
623,991✔
571

572
    // Fill up if indexes are not consecutive
573
    while (size() < ndx)
623,991✔
574
        Array::add(0);
×
575

576
    if (ndx == size())
623,991✔
577
        Array::insert(ndx, from_ref(arr.get_ref()));
623,901✔
578
    else
90✔
579
        Array::set(ndx, from_ref(arr.get_ref()));
90✔
580
}
623,991✔
581

582
void Cluster::insert_column(ColKey col_key)
583
{
820,020✔
584
    auto attr = col_key.get_attrs();
820,020✔
585
    auto type = col_key.get_type();
820,020✔
586
    if (attr.test(col_attr_Collection)) {
820,020✔
587
        size_t sz = node_size();
196,029✔
588

589
        ArrayRef arr(m_alloc);
196,029✔
590
        arr.create(sz);
196,029✔
591
        auto col_ndx = col_key.get_index();
196,029✔
592
        unsigned idx = col_ndx.val + s_first_col_index;
196,029✔
593
        if (idx == size())
196,029✔
594
            Array::insert(idx, from_ref(arr.get_ref()));
196,029✔
595
        else
×
596
            Array::set(idx, from_ref(arr.get_ref()));
×
597
        return;
196,029✔
598
    }
196,029✔
599
    bool nullable = attr.test(col_attr_Nullable);
623,991✔
600
    switch (type) {
623,991✔
601
        case col_type_Int:
280,968✔
602
            if (nullable) {
280,968✔
603
                do_insert_column<ArrayIntNull>(col_key, nullable);
32,979✔
604
            }
32,979✔
605
            else {
247,989✔
606
                do_insert_column<ArrayInteger>(col_key, nullable);
247,989✔
607
            }
247,989✔
608
            break;
280,968✔
609
        case col_type_Bool:
5,490✔
610
            do_insert_column<ArrayBoolNull>(col_key, nullable);
5,490✔
611
            break;
5,490✔
612
        case col_type_Float:
6,447✔
613
            do_insert_column<ArrayFloatNull>(col_key, nullable);
6,447✔
614
            break;
6,447✔
615
        case col_type_Double:
7,080✔
616
            do_insert_column<ArrayDoubleNull>(col_key, nullable);
7,080✔
617
            break;
7,080✔
618
        case col_type_String:
104,745✔
619
            do_insert_column<ArrayString>(col_key, nullable);
104,745✔
620
            break;
104,745✔
621
        case col_type_Binary:
7,260✔
622
            do_insert_column<ArrayBinary>(col_key, nullable);
7,260✔
623
            break;
7,260✔
624
        case col_type_Mixed:
9,108✔
625
            do_insert_column<ArrayMixed>(col_key, nullable);
9,108✔
626
            break;
9,108✔
627
        case col_type_Timestamp:
24,435✔
628
            do_insert_column<ArrayTimestamp>(col_key, nullable);
24,435✔
629
            break;
24,435✔
630
        case col_type_Decimal:
5,022✔
631
            do_insert_column<ArrayDecimal128>(col_key, nullable);
5,022✔
632
            break;
5,022✔
633
        case col_type_ObjectId:
51,507✔
634
            do_insert_column<ArrayObjectIdNull>(col_key, nullable);
51,507✔
635
            break;
51,507✔
636
        case col_type_UUID:
5,616✔
637
            do_insert_column<ArrayUUIDNull>(col_key, nullable);
5,616✔
638
            break;
5,616✔
639
        case col_type_Link:
31,428✔
640
            do_insert_column<ArrayKey>(col_key, nullable);
31,428✔
641
            break;
31,428✔
642
        case col_type_TypedLink:
✔
643
            do_insert_column<ArrayTypedLink>(col_key, nullable);
×
644
            break;
×
645
        case col_type_BackLink:
84,885✔
646
            do_insert_column<ArrayBacklink>(col_key, nullable);
84,885✔
647
            break;
84,885✔
648
        default:
✔
649
            REALM_UNREACHABLE();
650
            break;
×
651
    }
623,991✔
652
}
623,991✔
653

654
void Cluster::remove_column(ColKey col_key)
655
{
6,606✔
656
    auto col_ndx = col_key.get_index();
6,606✔
657
    unsigned idx = col_ndx.val + s_first_col_index;
6,606✔
658
    ref_type ref = to_ref(Array::get(idx));
6,606✔
659
    if (ref != 0) {
6,606✔
660
        Array::destroy_deep(ref, m_alloc);
6,606✔
661
    }
6,606✔
662
    if (idx == size() - 1)
6,606✔
663
        Array::erase(idx);
5,073✔
664
    else
1,533✔
665
        Array::set(idx, 0);
1,533✔
666
}
6,606✔
667

668
ref_type Cluster::insert(RowKey row_key, const FieldValues& init_values, ClusterNode::State& state)
669
{
23,840,760✔
670
    int64_t current_key_value = -1;
23,840,760✔
671
    size_t sz;
23,840,760✔
672
    size_t ndx;
23,840,760✔
673
    ref_type ret = 0;
23,840,760✔
674

675
    auto on_error = [&] {
23,840,760✔
676
        throw KeyAlreadyUsed(
12✔
677
            util::format("When inserting key '%1' in '%2'", row_key.value, get_owning_table()->get_name()));
12✔
678
    };
12✔
679

680
    if (m_keys.is_attached()) {
23,840,760✔
681
        sz = m_keys.size();
1,616,655✔
682
        ndx = m_keys.lower_bound(row_key.value);
1,616,655✔
683
        if (ndx < sz) {
1,616,655✔
684
            current_key_value = m_keys.get(ndx);
616,350✔
685
            if (row_key.value == uint64_t(current_key_value)) {
616,350✔
686
                on_error();
12✔
687
            }
12✔
688
        }
616,350✔
689
    }
1,616,655✔
690
    else {
22,224,105✔
691
        sz = size_t(Array::get(s_key_ref_or_size_index)) >> 1; // Size is stored as tagged integer
22,224,105✔
692
        if (row_key.value < sz) {
22,224,105✔
693
            on_error();
×
694
        }
×
695
        // Key value is bigger than all other values, should be put last
696
        ndx = sz;
22,224,105✔
697
        if (row_key.value > sz && sz < cluster_node_size) {
22,224,105✔
698
            ensure_general_form();
18,006✔
699
        }
18,006✔
700
    }
22,224,105✔
701

702
    REALM_ASSERT_DEBUG(sz <= cluster_node_size);
23,840,760✔
703
    if (REALM_LIKELY(sz < cluster_node_size)) {
23,840,760✔
704
        insert_row(ndx, row_key, init_values); // Throws
23,736,615✔
705
        state.mem = get_mem();
23,736,615✔
706
        state.index = ndx;
23,736,615✔
707
    }
23,736,615✔
708
    else {
104,145✔
709
        // Split leaf node
710
        Cluster new_leaf(0, m_alloc, m_tree_top);
104,145✔
711
        new_leaf.create();
104,145✔
712
        if (ndx == sz) {
106,488✔
713
            new_leaf.insert_row(0, RowKey(0), init_values); // Throws
86,595✔
714
            state.split_key = int64_t(row_key.value);
86,595✔
715
            state.mem = new_leaf.get_mem();
86,595✔
716
            state.index = 0;
86,595✔
717
        }
86,595✔
718
        else {
2,147,503,540✔
719
            // Current cluster must be in general form to get here
720
            REALM_ASSERT_DEBUG(m_keys.is_attached());
2,147,503,540✔
721
            new_leaf.ensure_general_form();
2,147,503,540✔
722
            move(ndx, &new_leaf, current_key_value);
2,147,503,540✔
723
            insert_row(ndx, row_key, init_values); // Throws
2,147,503,540✔
724
            state.mem = get_mem();
2,147,503,540✔
725
            state.split_key = current_key_value;
2,147,503,540✔
726
            state.index = ndx;
2,147,503,540✔
727
        }
2,147,503,540✔
728
        ret = new_leaf.get_ref();
104,145✔
729
    }
104,145✔
730

731
    return ret;
23,840,760✔
732
}
23,840,760✔
733

734
bool Cluster::try_get(RowKey k, ClusterNode::State& state) const noexcept
735
{
168,949,455✔
736
    state.mem = get_mem();
168,949,455✔
737
    if (m_keys.is_attached()) {
168,949,455✔
738
        state.index = m_keys.lower_bound(k.value);
51,314,448✔
739
        return state.index != m_keys.size() && m_keys.get(state.index) == k.value;
51,314,448✔
740
    }
51,314,448✔
741
    else {
117,635,007✔
742
        if (k.value < uint64_t(Array::get(s_key_ref_or_size_index) >> 1)) {
117,635,007✔
743
            state.index = size_t(k.value);
98,874,120✔
744
            return true;
98,874,120✔
745
        }
98,874,120✔
746
    }
117,635,007✔
747
    return false;
18,760,887✔
748
}
168,949,455✔
749

750
ObjKey Cluster::get(size_t ndx, ClusterNode::State& state) const
751
{
11,691,153✔
752
    state.index = ndx;
11,691,153✔
753
    state.mem = get_mem();
11,691,153✔
754
    return get_real_key(ndx);
11,691,153✔
755
}
11,691,153✔
756

757
template <class T>
758
inline void Cluster::do_erase(size_t ndx, ColKey col_key)
759
{
7,100,730✔
760
    auto col_ndx = col_key.get_index();
7,100,730✔
761
    T values(m_alloc);
7,100,730✔
762
    values.set_parent(this, col_ndx.val + s_first_col_index);
7,100,730✔
763
    set_spec<T>(values, col_ndx);
7,100,730✔
764
    values.init_from_parent();
7,100,730✔
765
    if constexpr (std::is_same_v<T, ArrayTypedLink>) {
7,100,730✔
766
        if (ObjLink link = values.get(ndx)) {
×
767
            if (const Table* origin_table = m_tree_top.get_owning_table()) {
×
768
                auto target_obj = origin_table->get_parent_group()->get_object(link);
×
769

770
                ColKey backlink_col_key =
×
771
                    target_obj.get_table()->find_backlink_column(col_key, origin_table->get_key());
×
772
                REALM_ASSERT(backlink_col_key);
×
773
                target_obj.remove_one_backlink(backlink_col_key, get_real_key(ndx)); // Throws
×
774
            }
×
775
        }
×
776
    }
×
777
    values.erase(ndx);
7,100,730✔
778
}
7,100,730✔
779

780
inline void Cluster::do_erase_mixed(size_t ndx, ColKey col_key, CascadeState& state)
781
{
145,431✔
782
    const Table* origin_table = m_tree_top.get_owning_table();
145,431✔
783
    auto col_ndx = col_key.get_index();
145,431✔
784

785
    ArrayMixed values(m_alloc);
145,431✔
786
    values.set_parent(this, col_ndx.val + s_first_col_index);
145,431✔
787
    values.init_from_parent();
145,431✔
788

789
    Mixed value = values.get(ndx);
145,431✔
790
    if (value.is_type(type_TypedLink)) {
145,431✔
791
        ObjLink link = value.get<ObjLink>();
30✔
792
        Obj obj(origin_table->m_own_ref, get_mem(), get_real_key(ndx), ndx);
30✔
793
        obj.remove_backlink(col_key, link, state);
30✔
794
    }
30✔
795
    if (value.is_type(type_List)) {
145,431✔
796
        Obj obj(origin_table->m_own_ref, get_mem(), get_real_key(ndx), ndx);
72,033✔
797
        Lst<Mixed> list(obj, col_key);
72,033✔
798
        list.remove_backlinks(state);
72,033✔
799
    }
72,033✔
800
    if (value.is_type(type_Dictionary)) {
145,431✔
801
        Obj obj(origin_table->m_own_ref, get_mem(), get_real_key(ndx), ndx);
72,018✔
802
        Dictionary dict(obj, col_key);
72,018✔
803
        dict.remove_backlinks(state);
72,018✔
804
    }
72,018✔
805
    values.erase(ndx);
145,431✔
806
}
145,431✔
807

808
inline void Cluster::do_erase_key(size_t ndx, ColKey col_key, CascadeState& state)
809
{
92,838✔
810
    ArrayKey values(m_alloc);
92,838✔
811
    auto col_ndx = col_key.get_index();
92,838✔
812
    values.set_parent(this, col_ndx.val + s_first_col_index);
92,838✔
813
    values.init_from_parent();
92,838✔
814

815
    ObjKey key = values.get(ndx);
92,838✔
816
    if (key != null_key) {
92,838✔
817
        do_remove_backlinks(get_real_key(ndx), col_key, std::vector<ObjKey>{key}, state);
90,078✔
818
    }
90,078✔
819
    values.erase(ndx);
92,838✔
820
}
92,838✔
821

822
size_t Cluster::get_ndx(RowKey k, size_t ndx) const noexcept
823
{
10,551,483✔
824
    size_t index;
10,551,483✔
825
    if (m_keys.is_attached()) {
10,551,483✔
826
        index = m_keys.lower_bound(k.value);
10,432,350✔
827
        if (index == m_keys.size() || m_keys.get(index) != k.value) {
10,432,950✔
828
            return realm::npos;
18✔
829
        }
18✔
830
    }
10,432,350✔
831
    else {
119,133✔
832
        index = size_t(k.value);
119,133✔
833
        if (index >= get_as_ref_or_tagged(s_key_ref_or_size_index).get_as_int()) {
119,133✔
834
            return realm::npos;
×
835
        }
×
836
    }
119,133✔
837
    return index + ndx;
10,551,465✔
838
}
10,551,483✔
839

840
size_t Cluster::erase(RowKey row_key, CascadeState& state)
841
{
5,322,924✔
842
    size_t ndx = get_ndx(row_key, 0);
5,322,924✔
843
    if (ndx == realm::npos)
5,322,924✔
844
        throw KeyNotFound(util::format("When erasing key '%1' (offset '%2') in '%3'", row_key.value, m_offset,
18✔
845
                                       get_owning_table()->get_name()));
18✔
846

847
    ObjKey real_key = get_real_key(ndx);
5,322,906✔
848
    std::vector<ColKey> backlink_column_keys;
5,322,906✔
849

850
    auto erase_in_column = [&](ColKey col_key) {
7,443,870✔
851
        auto col_type = col_key.get_type();
7,443,870✔
852
        auto attr = col_key.get_attrs();
7,443,870✔
853
        if (attr.test(col_attr_Collection)) {
7,443,870✔
854
            auto col_ndx = col_key.get_index();
105,171✔
855
            ArrayRef values(m_alloc);
105,171✔
856
            values.set_parent(this, col_ndx.val + s_first_col_index);
105,171✔
857
            values.init_from_parent();
105,171✔
858
            ref_type ref = values.get(ndx);
105,171✔
859

860
            if (ref) {
105,171✔
861
                const Table* origin_table = m_tree_top.get_owning_table();
84,198✔
862
                if (attr.test(col_attr_Dictionary)) {
84,198✔
863
                    if (col_type == col_type_Mixed || col_type == col_type_Link) {
38,655✔
864
                        Obj obj(origin_table->m_own_ref, get_mem(), real_key, ndx);
36,354✔
865
                        Dictionary dict(obj, col_key);
36,354✔
866
                        dict.remove_backlinks(state);
36,354✔
867
                    }
36,354✔
868
                }
38,655✔
869
                else if (col_type == col_type_Link) {
45,543✔
870
                    BPlusTree<ObjKey> links(m_alloc);
4,677✔
871
                    links.init_from_ref(ref);
4,677✔
872
                    if (links.size() > 0) {
4,677✔
873
                        do_remove_backlinks(real_key, col_key, links.get_all(), state);
1,041✔
874
                    }
1,041✔
875
                }
4,677✔
876
                else if (col_type == col_type_TypedLink) {
40,866✔
877
                    BPlusTree<ObjLink> links(m_alloc);
×
878
                    links.init_from_ref(ref);
×
879
                    for (size_t i = 0; i < links.size(); i++) {
×
880
                        ObjLink link = links.get(i);
×
881
                        auto target_obj = origin_table->get_parent_group()->get_object(link);
×
882
                        ColKey backlink_col_key =
×
883
                            target_obj.get_table()->find_backlink_column(col_key, origin_table->get_key());
×
884
                        target_obj.remove_one_backlink(backlink_col_key, real_key);
×
885
                    }
×
886
                }
×
887
                else if (col_type == col_type_Mixed) {
40,866✔
888
                    Obj obj(origin_table->m_own_ref, get_mem(), real_key, ndx);
36,246✔
889
                    Lst<Mixed> list(obj, col_key);
36,246✔
890
                    list.remove_backlinks(state);
36,246✔
891
                }
36,246✔
892
                Array::destroy_deep(ref, m_alloc);
84,198✔
893
            }
84,198✔
894

895
            values.erase(ndx);
105,171✔
896

897
            return IteratorControl::AdvanceToNext;
105,171✔
898
        }
105,171✔
899

900
        switch (col_type) {
7,338,699✔
901
            case col_type_Int:
6,043,686✔
902
                if (attr.test(col_attr_Nullable)) {
6,043,686✔
903
                    do_erase<ArrayIntNull>(ndx, col_key);
1,275,831✔
904
                }
1,275,831✔
905
                else {
4,767,855✔
906
                    do_erase<ArrayInteger>(ndx, col_key);
4,767,855✔
907
                }
4,767,855✔
908
                break;
6,043,686✔
909
            case col_type_Bool:
42,534✔
910
                do_erase<ArrayBoolNull>(ndx, col_key);
42,534✔
911
                break;
42,534✔
912
            case col_type_Float:
36,489✔
913
                do_erase<ArrayFloatNull>(ndx, col_key);
36,489✔
914
                break;
36,489✔
915
            case col_type_Double:
36,501✔
916
                do_erase<ArrayDoubleNull>(ndx, col_key);
36,501✔
917
                break;
36,501✔
918
            case col_type_String:
427,608✔
919
                do_erase<ArrayString>(ndx, col_key);
427,608✔
920
                break;
427,608✔
921
            case col_type_Binary:
43,575✔
922
                do_erase<ArrayBinary>(ndx, col_key);
43,575✔
923
                break;
43,575✔
924
            case col_type_Mixed:
145,431✔
925
                do_erase_mixed(ndx, col_key, state);
145,431✔
926
                break;
145,431✔
927
            case col_type_Timestamp:
45,408✔
928
                do_erase<ArrayTimestamp>(ndx, col_key);
45,408✔
929
                break;
45,408✔
930
            case col_type_Decimal:
36,279✔
931
                do_erase<ArrayDecimal128>(ndx, col_key);
36,279✔
932
                break;
36,279✔
933
            case col_type_ObjectId:
73,959✔
934
                do_erase<ArrayObjectIdNull>(ndx, col_key);
73,959✔
935
                break;
73,959✔
936
            case col_type_UUID:
60,285✔
937
                do_erase<ArrayUUIDNull>(ndx, col_key);
60,285✔
938
                break;
60,285✔
939
            case col_type_Link:
92,838✔
940
                do_erase_key(ndx, col_key, state);
92,838✔
941
                break;
92,838✔
942
            case col_type_TypedLink:
✔
943
                do_erase<ArrayTypedLink>(ndx, col_key);
×
944
                break;
×
945
            case col_type_BackLink:
254,403✔
946
                if (state.m_mode == CascadeState::Mode::None) {
254,403✔
947
                    do_erase<ArrayBacklink>(ndx, col_key);
188,622✔
948
                }
188,622✔
949
                else {
65,781✔
950
                    // Postpone the deletion of backlink entries or else the
951
                    // checks for if there's any remaining backlinks will
952
                    // check the wrong row for columns which have already
953
                    // had values erased from them.
954
                    backlink_column_keys.push_back(col_key);
65,781✔
955
                }
65,781✔
956
                break;
254,403✔
957
            default:
✔
958
                REALM_ASSERT(false);
×
959
                break;
×
960
        }
7,338,699✔
961
        return IteratorControl::AdvanceToNext;
7,338,597✔
962
    };
7,338,699✔
963
    m_tree_top.m_owner->for_each_and_every_column(erase_in_column);
5,322,906✔
964

965
    // Any remaining backlink columns to erase from?
966
    for (auto k : backlink_column_keys)
5,322,906✔
967
        do_erase<ArrayBacklink>(ndx, k);
65,781✔
968

969
    if (m_keys.is_attached()) {
5,322,906✔
970
        m_keys.erase(ndx);
5,279,922✔
971
    }
5,279,922✔
972
    else {
42,984✔
973
        size_t current_size = get_size_in_compact_form();
42,984✔
974
        if (ndx == current_size - 1) {
42,984✔
975
            // When deleting last, we can still maintain compact form
976
            set(0, RefOrTagged::make_tagged(current_size - 1));
27,117✔
977
        }
27,117✔
978
        else {
15,867✔
979
            ensure_general_form();
15,867✔
980
            m_keys.erase(ndx);
15,867✔
981
        }
15,867✔
982
    }
42,984✔
983

984
    return node_size();
5,322,906✔
985
}
5,322,924✔
986

987
void Cluster::nullify_incoming_links(RowKey key, CascadeState& state)
988
{
5,192,286✔
989
    size_t ndx = get_ndx(key, 0);
5,192,286✔
990
    if (ndx == realm::npos)
5,192,286✔
991
        throw KeyNotFound(util::format("Key '%1' not found in '%2' when nullifying incoming links", key.value,
×
992
                                       get_owning_table()->get_class_name()));
×
993

994
    // We must start with backlink columns in case the corresponding link
995
    // columns are in the same table so that we can nullify links before
996
    // erasing rows in the link columns.
997
    //
998
    // This phase also generates replication instructions documenting the side-
999
    // effects of deleting the object (i.e. link nullifications). These instructions
1000
    // must come before the actual deletion of the object, but at the same time
1001
    // the Replication object may need a consistent view of the row (not including
1002
    // link columns). Therefore we first nullify links to this object, then
1003
    // generate the instruction, and then delete the row in the remaining columns.
1004

1005
    auto nullify_fwd_links = [&](ColKey col_key) {
5,192,286✔
1006
        ColKey::Idx leaf_ndx = col_key.get_index();
233,127✔
1007
        auto type = col_key.get_type();
233,127✔
1008
        REALM_ASSERT(type == col_type_BackLink);
233,127✔
1009
        ArrayBacklink values(m_alloc);
233,127✔
1010
        values.set_parent(this, leaf_ndx.val + s_first_col_index);
233,127✔
1011
        values.init_from_parent();
233,127✔
1012
        // Ensure that Cluster is writable and able to hold references to nodes in
1013
        // the slab area before nullifying or deleting links. These operation may
1014
        // both have the effect that other objects may be constructed and manipulated.
1015
        // If those other object are in the same cluster that the object to be deleted
1016
        // is in, then that will cause another accessor to this cluster to be created.
1017
        // It would lead to an error if the cluster node was relocated without it being
1018
        // reflected in the context here.
1019
        values.copy_on_write();
233,127✔
1020
        values.nullify_fwd_links(ndx, state);
233,127✔
1021

1022
        return IteratorControl::AdvanceToNext;
233,127✔
1023
    };
233,127✔
1024

1025
    m_tree_top.get_owning_table()->for_each_backlink_column(nullify_fwd_links);
5,192,286✔
1026
}
5,192,286✔
1027

1028
void Cluster::upgrade_string_to_enum(ColKey col_key, ArrayString& keys)
1029
{
1,089✔
1030
    auto col_ndx = col_key.get_index();
1,089✔
1031
    Array indexes(m_alloc);
1,089✔
1032
    indexes.create(Array::type_Normal, false);
1,089✔
1033
    ArrayString values(m_alloc);
1,089✔
1034
    ref_type ref = Array::get_as_ref(col_ndx.val + s_first_col_index);
1,089✔
1035
    values.init_from_ref(ref);
1,089✔
1036
    size_t sz = values.size();
1,089✔
1037
    for (size_t i = 0; i < sz; i++) {
118,542✔
1038
        auto v = values.get(i);
117,453✔
1039
        size_t pos = keys.lower_bound(v);
117,453✔
1040
        REALM_ASSERT_3(pos, !=, keys.size());
117,453✔
1041
        indexes.add(pos);
117,453✔
1042
    }
117,453✔
1043
    Array::set(col_ndx.val + s_first_col_index, indexes.get_ref());
1,089✔
1044
    Array::destroy_deep(ref, m_alloc);
1,089✔
1045
}
1,089✔
1046

1047
void Cluster::init_leaf(ColKey col_key, ArrayPayload* leaf) const
1048
{
8,702,397✔
1049
    auto col_ndx = col_key.get_index();
8,702,397✔
1050
    // FIXME: Move this validation into callers.
1051
    // Currently, the query subsystem may call with an unvalidated key.
1052
    // once fixed, reintroduce the noexcept declaration :-D
1053
    if (auto t = m_tree_top.get_owning_table())
8,702,397✔
1054
        t->check_column(col_key);
8,505,762✔
1055
    ref_type ref = to_ref(Array::get(col_ndx.val + 1));
8,702,397✔
1056
    if (leaf->need_spec()) {
8,702,397✔
1057
        m_tree_top.set_spec(*leaf, col_ndx);
144,645✔
1058
    }
144,645✔
1059
    leaf->init_from_ref(ref);
8,702,397✔
1060
    leaf->set_parent(const_cast<Cluster*>(this), col_ndx.val + 1);
8,702,397✔
1061
}
8,702,397✔
1062

1063
void Cluster::add_leaf(ColKey col_key, ref_type ref)
1064
{
×
1065
    auto col_ndx = col_key.get_index();
×
1066
    REALM_ASSERT((col_ndx.val + 1) == size());
×
1067
    Array::insert(col_ndx.val + 1, from_ref(ref));
×
1068
}
×
1069

1070
template <typename ArrayType>
1071
void Cluster::verify(ref_type ref, size_t index, util::Optional<size_t>& sz) const
1072
{
534,537✔
1073
    ArrayType arr(get_alloc());
534,537✔
1074
    set_spec(arr, ColKey::Idx{unsigned(index) - 1});
534,537✔
1075
    arr.set_parent(const_cast<Cluster*>(this), index);
534,537✔
1076
    arr.init_from_ref(ref);
534,537✔
1077
    arr.verify();
534,537✔
1078
    if (sz) {
534,537!
1079
        REALM_ASSERT(arr.size() == *sz);
185,205✔
1080
    }
185,205✔
1081
    else {
349,332✔
1082
        sz = arr.size();
349,332✔
1083
    }
349,332✔
1084
}
534,537✔
1085
namespace {
1086

1087
template <typename ArrayType>
1088
void verify_list(ArrayRef& arr, size_t sz)
1089
{
111,690✔
1090
    for (size_t n = 0; n < sz; n++) {
398,496!
1091
        if (ref_type bp_tree_ref = arr.get(n)) {
286,806!
1092
            BPlusTree<ArrayType> links(arr.get_alloc());
112,221✔
1093
            links.init_from_ref(bp_tree_ref);
112,221✔
1094
            links.set_parent(&arr, n);
112,221✔
1095
            links.verify();
112,221✔
1096
        }
112,221✔
1097
    }
286,806✔
1098
}
111,690✔
1099

1100
template <typename SetType>
1101
void verify_set(ArrayRef& arr, size_t sz)
1102
{
174✔
1103
    for (size_t n = 0; n < sz; ++n) {
402!
1104
        if (ref_type bp_tree_ref = arr.get(n)) {
228!
1105
            BPlusTree<SetType> elements(arr.get_alloc());
216✔
1106
            elements.init_from_ref(bp_tree_ref);
216✔
1107
            elements.set_parent(&arr, n);
216✔
1108
            elements.verify();
216✔
1109

1110
            // FIXME: Check uniqueness of elements.
1111
        }
216✔
1112
    }
228✔
1113
}
174✔
1114

1115
} // namespace
1116

1117
void Cluster::verify() const
1118
{
386,214✔
1119
#ifdef REALM_DEBUG
386,214✔
1120
    util::Optional<size_t> sz;
386,214✔
1121

1122
    auto verify_column = [this, &sz](ColKey col_key) {
658,689✔
1123
        size_t col = col_key.get_index().val + s_first_col_index;
658,689✔
1124
        ref_type ref = Array::get_as_ref(col);
658,689✔
1125
        auto attr = col_key.get_attrs();
658,689✔
1126
        auto col_type = col_key.get_type();
658,689✔
1127
        bool nullable = attr.test(col_attr_Nullable);
658,689✔
1128

1129
        if (attr.test(col_attr_List)) {
658,689✔
1130
            ArrayRef arr(get_alloc());
111,708✔
1131
            arr.set_parent(const_cast<Cluster*>(this), col);
111,708✔
1132
            arr.init_from_ref(ref);
111,708✔
1133
            arr.verify();
111,708✔
1134
            if (sz) {
111,708✔
1135
                REALM_ASSERT(arr.size() == *sz);
87,342✔
1136
            }
87,342✔
1137
            else {
24,366✔
1138
                sz = arr.size();
24,366✔
1139
            }
24,366✔
1140

1141
            switch (col_type) {
111,708✔
1142
                case col_type_Int:
35,229✔
1143
                    if (nullable) {
35,229✔
1144
                        verify_list<util::Optional<int64_t>>(arr, *sz);
×
1145
                    }
×
1146
                    else {
35,229✔
1147
                        verify_list<int64_t>(arr, *sz);
35,229✔
1148
                    }
35,229✔
1149
                    break;
35,229✔
1150
                case col_type_Bool:
✔
1151
                    verify_list<Bool>(arr, *sz);
×
1152
                    break;
×
1153
                case col_type_Float:
6✔
1154
                    verify_list<Float>(arr, *sz);
6✔
1155
                    break;
6✔
1156
                case col_type_Double:
✔
1157
                    verify_list<Double>(arr, *sz);
×
1158
                    break;
×
1159
                case col_type_String:
51,243✔
1160
                    verify_list<String>(arr, *sz);
51,243✔
1161
                    break;
51,243✔
1162
                case col_type_Binary:
24,000✔
1163
                    verify_list<Binary>(arr, *sz);
24,000✔
1164
                    break;
24,000✔
1165
                case col_type_Timestamp:
✔
1166
                    verify_list<Timestamp>(arr, *sz);
×
1167
                    break;
×
1168
                case col_type_Decimal:
✔
1169
                    verify_list<Decimal128>(arr, *sz);
×
1170
                    break;
×
1171
                case col_type_ObjectId:
✔
1172
                    verify_list<ObjectId>(arr, *sz);
×
1173
                    break;
×
1174
                case col_type_UUID:
✔
1175
                    verify_list<UUID>(arr, *sz);
×
1176
                    break;
×
1177
                case col_type_Link:
1,212✔
1178
                    verify_list<ObjKey>(arr, *sz);
1,212✔
1179
                    break;
1,212✔
1180
                default:
18✔
1181
                    // FIXME: Nullable primitives
1182
                    break;
18✔
1183
            }
111,708✔
1184
            return IteratorControl::AdvanceToNext;
111,708✔
1185
        }
111,708✔
1186
        else if (attr.test(col_attr_Dictionary)) {
546,981✔
1187
            ArrayRef arr(get_alloc());
12,168✔
1188
            arr.set_parent(const_cast<Cluster*>(this), col);
12,168✔
1189
            arr.init_from_ref(ref);
12,168✔
1190
            arr.verify();
12,168✔
1191
            if (sz) {
12,168✔
1192
                REALM_ASSERT(arr.size() == *sz);
138✔
1193
            }
138✔
1194
            else {
12,030✔
1195
                sz = arr.size();
12,030✔
1196
            }
12,030✔
1197
            for (size_t n = 0; n < sz; n++) {
24,504✔
1198
                if (auto ref = arr.get(n)) {
12,336✔
1199
                    Dictionary dict(get_alloc(), col_key, to_ref(ref));
12,222✔
1200
                    dict.verify();
12,222✔
1201
                }
12,222✔
1202
            }
12,336✔
1203
            return IteratorControl::AdvanceToNext;
12,168✔
1204
        }
12,168✔
1205
        else if (attr.test(col_attr_Set)) {
534,813✔
1206
            ArrayRef arr(get_alloc());
270✔
1207
            arr.set_parent(const_cast<Cluster*>(this), col);
270✔
1208
            arr.init_from_ref(ref);
270✔
1209
            arr.verify();
270✔
1210
            if (sz) {
270✔
1211
                REALM_ASSERT(arr.size() == *sz);
252✔
1212
            }
252✔
1213
            else {
18✔
1214
                sz = arr.size();
18✔
1215
            }
18✔
1216
            switch (col_type) {
270✔
1217
                case col_type_Int:
138✔
1218
                    if (nullable) {
138✔
1219
                        verify_set<util::Optional<int64_t>>(arr, *sz);
×
1220
                    }
×
1221
                    else {
138✔
1222
                        verify_set<int64_t>(arr, *sz);
138✔
1223
                    }
138✔
1224
                    break;
138✔
1225
                case col_type_Bool:
✔
1226
                    verify_set<Bool>(arr, *sz);
×
1227
                    break;
×
1228
                case col_type_Float:
✔
1229
                    verify_set<Float>(arr, *sz);
×
1230
                    break;
×
1231
                case col_type_Double:
✔
1232
                    verify_set<Double>(arr, *sz);
×
1233
                    break;
×
1234
                case col_type_String:
6✔
1235
                    verify_set<String>(arr, *sz);
6✔
1236
                    break;
6✔
1237
                case col_type_Binary:
18✔
1238
                    verify_set<Binary>(arr, *sz);
18✔
1239
                    break;
18✔
1240
                case col_type_Timestamp:
✔
1241
                    verify_set<Timestamp>(arr, *sz);
×
1242
                    break;
×
1243
                case col_type_Decimal:
✔
1244
                    verify_set<Decimal128>(arr, *sz);
×
1245
                    break;
×
1246
                case col_type_ObjectId:
✔
1247
                    verify_set<ObjectId>(arr, *sz);
×
1248
                    break;
×
1249
                case col_type_UUID:
✔
1250
                    verify_set<UUID>(arr, *sz);
×
1251
                    break;
×
1252
                case col_type_Link:
12✔
1253
                    verify_set<ObjKey>(arr, *sz);
12✔
1254
                    break;
12✔
1255
                default:
96✔
1256
                    // FIXME: Nullable primitives
1257
                    break;
96✔
1258
            }
270✔
1259
            return IteratorControl::AdvanceToNext;
270✔
1260
        }
270✔
1261

1262
        switch (col_type) {
534,543✔
1263
            case col_type_Int:
320,487✔
1264
                if (nullable) {
320,487✔
1265
                    verify<ArrayIntNull>(ref, col, sz);
24,159✔
1266
                }
24,159✔
1267
                else {
296,328✔
1268
                    verify<ArrayInteger>(ref, col, sz);
296,328✔
1269
                }
296,328✔
1270
                break;
320,487✔
1271
            case col_type_Bool:
7,203✔
1272
                verify<ArrayBoolNull>(ref, col, sz);
7,203✔
1273
                break;
7,203✔
1274
            case col_type_Float:
222✔
1275
                verify<ArrayFloatNull>(ref, col, sz);
222✔
1276
                break;
222✔
1277
            case col_type_Double:
156✔
1278
                verify<ArrayDoubleNull>(ref, col, sz);
156✔
1279
                break;
156✔
1280
            case col_type_String:
142,689✔
1281
                verify<ArrayString>(ref, col, sz);
142,689✔
1282
                break;
142,689✔
1283
            case col_type_Binary:
49,872✔
1284
                verify<ArrayBinary>(ref, col, sz);
49,872✔
1285
                break;
49,872✔
1286
            case col_type_Mixed:
2,688✔
1287
                verify<ArrayMixed>(ref, col, sz);
2,688✔
1288
                break;
2,688✔
1289
            case col_type_Timestamp:
6,996✔
1290
                verify<ArrayTimestamp>(ref, col, sz);
6,996✔
1291
                break;
6,996✔
1292
            case col_type_Decimal:
42✔
1293
                verify<ArrayDecimal128>(ref, col, sz);
42✔
1294
                break;
42✔
1295
            case col_type_ObjectId:
42✔
1296
                verify<ArrayObjectIdNull>(ref, col, sz);
42✔
1297
                break;
42✔
1298
            case col_type_UUID:
✔
1299
                verify<ArrayUUIDNull>(ref, col, sz);
×
1300
                break;
×
1301
            case col_type_Link:
1,560✔
1302
                verify<ArrayKey>(ref, col, sz);
1,560✔
1303
                break;
1,560✔
1304
            case col_type_BackLink:
2,580✔
1305
                verify<ArrayBacklink>(ref, col, sz);
2,580✔
1306
                break;
2,580✔
1307
            default:
✔
1308
                break;
×
1309
        }
534,543✔
1310
        return IteratorControl::AdvanceToNext;
534,537✔
1311
    };
534,543✔
1312

1313
    m_tree_top.m_owner->for_each_and_every_column(verify_column);
386,214✔
1314
#endif
386,214✔
1315
}
386,214✔
1316

1317
// LCOV_EXCL_START
1318
void Cluster::dump_objects(int64_t key_offset, std::string lead) const
1319
{
×
1320
    std::cout << lead << "leaf - size: " << node_size() << std::endl;
×
1321
    if (!m_keys.is_attached()) {
×
1322
        std::cout << lead << "compact form" << std::endl;
×
1323
    }
×
1324

1325
    for (unsigned i = 0; i < node_size(); i++) {
×
1326
        int64_t key_value;
×
1327
        if (m_keys.is_attached()) {
×
1328
            key_value = m_keys.get(i);
×
1329
        }
×
1330
        else {
×
1331
            key_value = int64_t(i);
×
1332
        }
×
1333
        std::cout << lead << "key: " << std::hex << key_value + key_offset << std::dec;
×
1334
        m_tree_top.m_owner->for_each_and_every_column([&](ColKey col) {
×
1335
            size_t j = col.get_index().val + 1;
×
1336
            if (col.get_attrs().test(col_attr_List)) {
×
1337
                ref_type ref = Array::get_as_ref(j);
×
1338
                ArrayRef refs(m_alloc);
×
1339
                refs.init_from_ref(ref);
×
1340
                std::cout << ", {";
×
1341
                ref = refs.get(i);
×
1342
                if (ref) {
×
1343
                    if (col.get_type() == col_type_Int) {
×
1344
                        // This is easy to handle
1345
                        Array ints(m_alloc);
×
1346
                        ints.init_from_ref(ref);
×
1347
                        for (size_t n = 0; n < ints.size(); n++) {
×
1348
                            std::cout << ints.get(n) << ", ";
×
1349
                        }
×
1350
                    }
×
1351
                    else {
×
1352
                        std::cout << col.get_type();
×
1353
                    }
×
1354
                }
×
1355
                std::cout << "}";
×
1356
                return IteratorControl::AdvanceToNext;
×
1357
            }
×
1358

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

1501
void Cluster::remove_backlinks(const Table* origin_table, ObjKey origin_key, ColKey origin_col_key,
1502
                               const std::vector<ObjKey>& keys, CascadeState& state)
1503
{
91,887✔
1504
    TableRef target_table = origin_table->get_opposite_table(origin_col_key);
91,887✔
1505
    ColKey backlink_col_key = origin_table->get_opposite_column(origin_col_key);
91,887✔
1506
    bool strong_links = target_table->is_embedded();
91,887✔
1507

1508
    for (auto key : keys) {
93,585✔
1509
        if (key != null_key) {
93,585✔
1510
            bool is_unres = key.is_unresolved();
93,585✔
1511
            Obj target_obj = is_unres ? target_table->m_tombstones->get(key) : target_table->m_clusters.get(key);
93,585✔
1512
            bool last_removed = target_obj.remove_one_backlink(backlink_col_key, origin_key); // Throws
93,585✔
1513
            if (is_unres) {
93,585✔
1514
                if (last_removed) {
30✔
1515
                    // Check is there are more backlinks
1516
                    if (!target_obj.has_backlinks(false)) {
24✔
1517
                        // Tombstones can be erased right away - there is no cascading effect
1518
                        target_table->m_tombstones->erase(key, state);
18✔
1519
                    }
18✔
1520
                }
24✔
1521
            }
30✔
1522
            else {
93,555✔
1523
                state.enqueue_for_cascade(target_obj, strong_links, last_removed);
93,555✔
1524
            }
93,555✔
1525
        }
93,585✔
1526
    }
93,585✔
1527
}
91,887✔
1528

1529
void Cluster::remove_backlinks(const Table* origin_table, ObjKey origin_key, ColKey origin_col_key,
1530
                               const std::vector<ObjLink>& links, CascadeState& state)
1531
{
144✔
1532
    Group* group = origin_table->get_parent_group();
144✔
1533
    TableKey origin_table_key = origin_table->get_key();
144✔
1534

1535
    for (auto link : links) {
240✔
1536
        if (link) {
240✔
1537
            bool is_unres = link.get_obj_key().is_unresolved();
240✔
1538
            Obj target_obj = group->get_object(link);
240✔
1539
            TableRef target_table = target_obj.get_table();
240✔
1540
            ColKey backlink_col_key = target_table->find_or_add_backlink_column(origin_col_key, origin_table_key);
240✔
1541

1542
            bool last_removed = target_obj.remove_one_backlink(backlink_col_key, origin_key); // Throws
240✔
1543
            if (is_unres) {
240✔
1544
                if (last_removed) {
6✔
1545
                    // Check if there are more backlinks
1546
                    if (!target_obj.has_backlinks(false)) {
6✔
1547
                        // Tombstones can be erased right away - there is no cascading effect
1548
                        target_table->m_tombstones->erase(link.get_obj_key(), state);
6✔
1549
                    }
6✔
1550
                }
6✔
1551
            }
6✔
1552
            else {
234✔
1553
                state.enqueue_for_cascade(target_obj, false, last_removed);
234✔
1554
            }
234✔
1555
        }
240✔
1556
    }
240✔
1557
}
144✔
1558

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