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

realm / realm-core / github_pull_request_281750

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

Pull #6073

Evergreen

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

95488 of 175952 branches covered (0.0%)

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

622 existing lines in 51 files now uncovered.

233503 of 257934 relevant lines covered (90.53%)

6533720.56 hits per line

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

77.44
/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)
46
{
600,381✔
47
    if (m_values.size() > 1) {
600,381✔
48
        // Sort according to ColKey index
8,124✔
49
        std::sort(m_values.begin(), m_values.end(), [](const auto& a, const auto& b) {
48,639✔
50
            return a.col_key.get_index().val < b.col_key.get_index().val;
48,639✔
51
        });
48,639✔
52
    }
16,248✔
53
}
600,381✔
54

55
void FieldValues::insert(ColKey k, Mixed val, bool is_default)
56
{
505,212✔
57
    if (m_values.empty()) {
505,212✔
58
        m_values.emplace_back(k, val, is_default);
494,355✔
59
        return;
494,355✔
60
    }
494,355✔
61
    unsigned int idx = k.get_index().val;
10,857✔
62
    auto it = std::lower_bound(m_values.begin(), m_values.end(), idx, [](const auto& a, unsigned int i) {
21,042✔
63
        return a.col_key.get_index().val < i;
21,042✔
64
    });
21,042✔
65
    m_values.insert(it, {k, val, is_default});
10,857✔
66
}
10,857✔
67

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

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

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

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

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

97

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

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

173,328✔
105
    arr.add(RefOrTagged::make_tagged(0)); // Compact form
349,611✔
106
    return arr.get_mem();
349,611✔
107
}
349,611✔
108

109

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

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

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

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

207
void Cluster::init(MemRef mem)
208
{
190,103,187✔
209
    Array::init_from_mem(mem);
190,103,187✔
210
    auto rot = Array::get_as_ref_or_tagged(0);
190,103,187✔
211
    if (rot.is_tagged()) {
190,103,187✔
212
        m_keys.detach();
125,392,659✔
213
    }
125,392,659✔
214
    else {
64,710,528✔
215
        m_keys.init_from_ref(rot.get_as_ref());
64,710,528✔
216
    }
64,710,528✔
217
}
190,103,187✔
218

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

228
MemRef Cluster::ensure_writeable(ObjKey)
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(ObjKey, ref_type)
238
{
×
239
    REALM_UNREACHABLE();
×
240
}
×
241

242
size_t Cluster::node_size_from_header(Allocator& alloc, const char* header)
243
{
51,495,051✔
244
    auto rot = Array::get_as_ref_or_tagged(header, s_key_ref_or_size_index);
51,495,051✔
245
    if (rot.is_tagged()) {
51,495,051✔
246
        return size_t(rot.get_as_int());
51,259,038✔
247
    }
51,259,038✔
248
    else {
236,013✔
249
        return Array::get_size_from_header(alloc.translate(rot.get_as_ref()));
236,013✔
250
    }
236,013✔
251
}
51,495,051✔
252

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

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

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

15,929,541✔
269
    T arr(m_alloc);
32,231,499✔
270
    auto col_ndx = col.get_index();
32,231,499✔
271
    arr.set_parent(this, col_ndx.val + s_first_col_index);
32,231,499✔
272
    set_spec<T>(arr, col_ndx);
32,231,499✔
273
    arr.init_from_parent();
32,231,499✔
274
    if (init_val.is_null()) {
32,231,499✔
275
        arr.insert(ndx, T::default_value(nullable));
31,610,637✔
276
    }
31,610,637✔
277
    else {
620,862✔
278
        arr.insert(ndx, init_val.get<U>());
620,862✔
279
    }
620,862✔
280
}
32,231,499✔
281

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

124,566✔
291
    // Insert backlink if link is not null
124,566✔
292
    if (target_key) {
250,635✔
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
}
250,635✔
300

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

7,179✔
308
    // Insert backlink if needed
7,179✔
309
    if (init_value.is_type(type_TypedLink)) {
14,658✔
310
        // In case we are inserting in a Dictionary cluster, the backlink will
12✔
311
        // be handled in Dictionary::insert function
12✔
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
}
14,658✔
326

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

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

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

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

11,513,259✔
351
    if (m_keys.is_attached()) {
23,158,521✔
352
        m_keys.insert(ndx, k.value);
1,708,557✔
353
    }
1,708,557✔
354
    else {
21,449,964✔
355
        Array::set(s_key_ref_or_size_index, Array::get(s_key_ref_or_size_index) + 2); // Increments size by 1
21,449,964✔
356
    }
21,449,964✔
357

11,513,259✔
358
    auto val = init_values.begin();
23,158,521✔
359
    auto insert_in_column = [&](ColKey col_key) {
34,060,272✔
360
        auto col_ndx = col_key.get_index();
34,060,272✔
361
        auto attr = col_key.get_attrs();
34,060,272✔
362
        Mixed init_value;
34,060,272✔
363
        // init_values must be sorted in col_ndx order - this is ensured by ClustTree::insert()
16,826,700✔
364
        if (val != init_values.end() && val->col_key.get_index().val == col_ndx.val) {
34,060,272✔
365
            init_value = val->value;
582,381✔
366
            ++val;
582,381✔
367
        }
582,381✔
368

16,826,700✔
369
        auto type = col_key.get_type();
34,060,272✔
370
        if (attr.test(col_attr_Collection)) {
34,060,272✔
371
            REALM_ASSERT(init_value.is_null());
664,959✔
372
            ArrayRef arr(m_alloc);
664,959✔
373
            arr.set_parent(this, col_ndx.val + s_first_col_index);
664,959✔
374
            arr.init_from_parent();
664,959✔
375
            arr.insert(ndx, 0);
664,959✔
376
            return IteratorControl::AdvanceToNext;
664,959✔
377
        }
664,959✔
378

16,495,716✔
379
        bool nullable = attr.test(col_attr_Nullable);
33,395,313✔
380
        switch (type) {
33,395,313✔
381
            case col_type_Int:
25,642,914✔
382
                if (attr.test(col_attr_Nullable)) {
25,642,914✔
383
                    do_insert_row<ArrayIntNull>(ndx, col_key, init_value, nullable);
1,387,224✔
384
                }
1,387,224✔
385
                else {
24,255,690✔
386
                    do_insert_row<ArrayInteger>(ndx, col_key, init_value, nullable);
24,255,690✔
387
                }
24,255,690✔
388
                break;
25,642,914✔
389
            case col_type_Bool:
186,639✔
390
                do_insert_row<ArrayBoolNull>(ndx, col_key, init_value, nullable);
186,639✔
391
                break;
186,639✔
392
            case col_type_Float:
320,445✔
393
                do_insert_row<ArrayFloatNull>(ndx, col_key, init_value, nullable);
320,445✔
394
                break;
320,445✔
395
            case col_type_Double:
1,489,965✔
396
                do_insert_row<ArrayDoubleNull>(ndx, col_key, init_value, nullable);
1,489,965✔
397
                break;
1,489,965✔
398
            case col_type_String:
2,710,152✔
399
                do_insert_row<ArrayString>(ndx, col_key, init_value, nullable);
2,710,152✔
400
                break;
2,710,152✔
401
            case col_type_Binary:
1,316,937✔
402
                do_insert_row<ArrayBinary>(ndx, col_key, init_value, nullable);
1,316,937✔
403
                break;
1,316,937✔
404
            case col_type_Mixed: {
14,658✔
405
                do_insert_mixed(ndx, col_key, init_value, ObjKey(k.value + get_offset()));
14,658✔
406
                break;
14,658✔
407
            }
×
408
            case col_type_Timestamp:
217,839✔
409
                do_insert_row<ArrayTimestamp>(ndx, col_key, init_value, nullable);
217,839✔
410
                break;
217,839✔
411
            case col_type_Decimal:
75,624✔
412
                do_insert_row<ArrayDecimal128>(ndx, col_key, init_value, nullable);
75,624✔
413
                break;
75,624✔
414
            case col_type_ObjectId:
162,633✔
415
                do_insert_row<ArrayObjectIdNull>(ndx, col_key, init_value, nullable);
162,633✔
416
                break;
162,633✔
417
            case col_type_UUID:
115,842✔
418
                do_insert_row<ArrayUUIDNull>(ndx, col_key, init_value, nullable);
115,842✔
419
                break;
115,842✔
420
            case col_type_Link:
250,638✔
421
                do_insert_key(ndx, col_key, init_value, ObjKey(k.value + get_offset()));
250,638✔
422
                break;
250,638✔
UNCOV
423
            case col_type_TypedLink:
✔
UNCOV
424
                do_insert_link(ndx, col_key, init_value, ObjKey(k.value + get_offset()));
×
UNCOV
425
                break;
×
426
            case col_type_BackLink: {
951,924✔
427
                ArrayBacklink arr(m_alloc);
951,924✔
428
                arr.set_parent(this, col_ndx.val + s_first_col_index);
951,924✔
429
                arr.init_from_parent();
951,924✔
430
                arr.insert(ndx, 0);
951,924✔
431
                break;
951,924✔
432
            }
×
433
            default:
✔
434
                REALM_ASSERT(false);
×
435
                break;
×
436
        }
33,257,295✔
437
        return IteratorControl::AdvanceToNext;
33,257,295✔
438
    };
33,257,295✔
439
    m_tree_top.m_owner->for_each_and_every_column(insert_in_column);
23,158,521✔
440
}
23,158,521✔
441

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

10,821✔
450
    T dst(m_alloc);
21,918✔
451
    dst.set_parent(to, col_ndx);
21,918✔
452
    dst.init_from_parent();
21,918✔
453

10,821✔
454
    src.move(dst, ndx);
21,918✔
455
}
21,918✔
456

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

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

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

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

535
Cluster::~Cluster() {}
192,274,287✔
536

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

545
void Cluster::ensure_general_form()
546
{
84,330✔
547
    if (!m_keys.is_attached()) {
84,330✔
548
        size_t current_size = get_size_in_compact_form();
70,998✔
549
        m_keys.create(current_size, 255);
70,998✔
550
        m_keys.update_parent();
70,998✔
551
        for (size_t i = 0; i < current_size; i++) {
5,556,324✔
552
            m_keys.set(i, i);
5,485,326✔
553
        }
5,485,326✔
554
    }
70,998✔
555
}
84,330✔
556

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

394,677✔
562
    T arr(m_alloc);
802,809✔
563
    arr.create();
802,809✔
564
    auto val = T::default_value(nullable);
802,809✔
565
    for (size_t i = 0; i < sz; i++) {
7,090,269!
566
        arr.add(val);
6,287,460✔
567
    }
6,287,460✔
568
    auto col_ndx = col_key.get_index();
802,809✔
569
    unsigned ndx = col_ndx.val + s_first_col_index;
802,809✔
570

394,677✔
571
    // Fill up if indexes are not consecutive
394,677✔
572
    while (size() < ndx)
802,809!
573
        Array::add(0);
×
574

394,677✔
575
    if (ndx == size())
802,809!
576
        Array::insert(ndx, from_ref(arr.get_ref()));
802,725✔
577
    else
84✔
578
        Array::set(ndx, from_ref(arr.get_ref()));
84✔
579
}
802,809✔
580

581
void Cluster::insert_column(ColKey col_key)
582
{
1,036,356✔
583
    auto attr = col_key.get_attrs();
1,036,356✔
584
    auto type = col_key.get_type();
1,036,356✔
585
    if (attr.test(col_attr_Collection)) {
1,036,356✔
586
        size_t sz = node_size();
233,550✔
587

116,190✔
588
        ArrayRef arr(m_alloc);
233,550✔
589
        arr.create(sz);
233,550✔
590
        auto col_ndx = col_key.get_index();
233,550✔
591
        unsigned idx = col_ndx.val + s_first_col_index;
233,550✔
592
        if (idx == size())
233,550✔
593
            Array::insert(idx, from_ref(arr.get_ref()));
233,550✔
594
        else
×
595
            Array::set(idx, from_ref(arr.get_ref()));
×
596
        return;
233,550✔
597
    }
233,550✔
598
    bool nullable = attr.test(col_attr_Nullable);
802,806✔
599
    switch (type) {
802,806✔
600
        case col_type_Int:
313,710✔
601
            if (nullable) {
313,710✔
602
                do_insert_column<ArrayIntNull>(col_key, nullable);
25,083✔
603
            }
25,083✔
604
            else {
288,627✔
605
                do_insert_column<ArrayInteger>(col_key, nullable);
288,627✔
606
            }
288,627✔
607
            break;
313,710✔
608
        case col_type_Bool:
5,412✔
609
            do_insert_column<ArrayBoolNull>(col_key, nullable);
5,412✔
610
            break;
5,412✔
611
        case col_type_Float:
6,348✔
612
            do_insert_column<ArrayFloatNull>(col_key, nullable);
6,348✔
613
            break;
6,348✔
614
        case col_type_Double:
6,960✔
615
            do_insert_column<ArrayDoubleNull>(col_key, nullable);
6,960✔
616
            break;
6,960✔
617
        case col_type_String:
265,371✔
618
            do_insert_column<ArrayString>(col_key, nullable);
265,371✔
619
            break;
265,371✔
620
        case col_type_Binary:
6,300✔
621
            do_insert_column<ArrayBinary>(col_key, nullable);
6,300✔
622
            break;
6,300✔
623
        case col_type_Mixed:
4,920✔
624
            do_insert_column<ArrayMixed>(col_key, nullable);
4,920✔
625
            break;
4,920✔
626
        case col_type_Timestamp:
20,805✔
627
            do_insert_column<ArrayTimestamp>(col_key, nullable);
20,805✔
628
            break;
20,805✔
629
        case col_type_Decimal:
4,938✔
630
            do_insert_column<ArrayDecimal128>(col_key, nullable);
4,938✔
631
            break;
4,938✔
632
        case col_type_ObjectId:
43,317✔
633
            do_insert_column<ArrayObjectIdNull>(col_key, nullable);
43,317✔
634
            break;
43,317✔
635
        case col_type_UUID:
5,526✔
636
            do_insert_column<ArrayUUIDNull>(col_key, nullable);
5,526✔
637
            break;
5,526✔
638
        case col_type_Link:
29,742✔
639
            do_insert_column<ArrayKey>(col_key, nullable);
29,742✔
640
            break;
29,742✔
UNCOV
641
        case col_type_TypedLink:
✔
UNCOV
642
            do_insert_column<ArrayTypedLink>(col_key, nullable);
×
UNCOV
643
            break;
×
644
        case col_type_BackLink:
89,460✔
645
            do_insert_column<ArrayBacklink>(col_key, nullable);
89,460✔
646
            break;
89,460✔
647
        default:
✔
648
            REALM_UNREACHABLE();
×
649
            break;
×
650
    }
802,806✔
651
}
802,806✔
652

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

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

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

11,487,621✔
679
    if (m_keys.is_attached()) {
23,124,984✔
680
        sz = m_keys.size();
1,680,084✔
681
        ndx = m_keys.lower_bound(uint64_t(k.value));
1,680,084✔
682
        if (ndx < sz) {
1,680,084✔
683
            current_key_value = m_keys.get(ndx);
621,243✔
684
            if (k.value == current_key_value) {
621,243✔
685
                on_error();
18✔
686
            }
18✔
687
        }
621,243✔
688
    }
1,680,084✔
689
    else {
21,444,900✔
690
        sz = size_t(Array::get(s_key_ref_or_size_index)) >> 1; // Size is stored as tagged integer
21,444,900✔
691
        if (uint64_t(k.value) < sz) {
21,444,900✔
692
            on_error();
×
693
        }
×
694
        // Key value is bigger than all other values, should be put last
10,649,490✔
695
        ndx = sz;
21,444,900✔
696
        if (uint64_t(k.value) > sz && sz < cluster_node_size) {
21,444,900✔
697
            ensure_general_form();
34,245✔
698
        }
34,245✔
699
    }
21,444,900✔
700

11,487,621✔
701
    REALM_ASSERT_DEBUG(sz <= cluster_node_size);
23,124,984✔
702
    if (REALM_LIKELY(sz < cluster_node_size)) {
23,124,984✔
703
        insert_row(ndx, k, init_values); // Throws
23,076,183✔
704
        state.mem = get_mem();
23,076,183✔
705
        state.index = ndx;
23,076,183✔
706
    }
23,076,183✔
707
    else {
48,801✔
708
        // Split leaf node
12,411✔
709
        Cluster new_leaf(0, m_alloc, m_tree_top);
48,801✔
710
        new_leaf.create();
48,801✔
711
        if (ndx == sz) {
83,766✔
712
            new_leaf.insert_row(0, ObjKey(0), init_values); // Throws
83,766✔
713
            state.split_key = k.value;
83,766✔
714
            state.mem = new_leaf.get_mem();
83,766✔
715
            state.index = 0;
83,766✔
716
        }
83,766✔
717
        else {
4,294,967,294✔
718
            // Current cluster must be in general form to get here
2,147,483,647✔
719
            REALM_ASSERT_DEBUG(m_keys.is_attached());
4,294,967,294✔
720
            new_leaf.ensure_general_form();
4,294,967,294✔
721
            move(ndx, &new_leaf, current_key_value);
4,294,967,294✔
722
            insert_row(ndx, k, init_values); // Throws
4,294,967,294✔
723
            state.mem = get_mem();
4,294,967,294✔
724
            state.split_key = current_key_value;
4,294,967,294✔
725
            state.index = ndx;
4,294,967,294✔
726
        }
4,294,967,294✔
727
        ret = new_leaf.get_ref();
48,801✔
728
    }
48,801✔
729

11,487,621✔
730
    return ret;
23,124,984✔
731
}
23,124,984✔
732

733
bool Cluster::try_get(ObjKey k, ClusterNode::State& state) const noexcept
734
{
147,341,064✔
735
    state.mem = get_mem();
147,341,064✔
736
    if (m_keys.is_attached()) {
147,341,064✔
737
        state.index = m_keys.lower_bound(uint64_t(k.value));
51,115,458✔
738
        return state.index != m_keys.size() && m_keys.get(state.index) == uint64_t(k.value);
51,115,458✔
739
    }
51,115,458✔
740
    else {
96,225,606✔
741
        if (uint64_t(k.value) < uint64_t(Array::get(s_key_ref_or_size_index) >> 1)) {
96,225,606✔
742
            state.index = size_t(k.value);
79,133,736✔
743
            return true;
79,133,736✔
744
        }
79,133,736✔
745
    }
17,091,870✔
746
    return false;
17,091,870✔
747
}
17,091,870✔
748

749
ObjKey Cluster::get(size_t ndx, ClusterNode::State& state) const
750
{
8,916,834✔
751
    state.index = ndx;
8,916,834✔
752
    state.mem = get_mem();
8,916,834✔
753
    return get_real_key(ndx);
8,916,834✔
754
}
8,916,834✔
755

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

12✔
778
            ColKey backlink_col_key = target_obj.get_table()->find_backlink_column(col_key, origin_table->get_key());
24✔
779
            REALM_ASSERT(backlink_col_key);
24!
780
            target_obj.remove_one_backlink(backlink_col_key, get_real_key(ndx)); // Throws
24✔
781
        }
24✔
782
    }
24✔
783
    values.erase(ndx);
6,744,120✔
784
}
6,744,120✔
785

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

33,573✔
793
    ObjKey key = values.get(ndx);
67,155✔
794
    if (key != null_key) {
67,155✔
795
        do_remove_backlinks(get_real_key(ndx), col_key, std::vector<ObjKey>{key}, state);
64,563✔
796
    }
64,563✔
797
    values.erase(ndx);
67,155✔
798
}
67,155✔
799

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

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

2,543,484✔
825
    auto erase_in_column = [&](ColKey col_key) {
6,863,907✔
826
        auto col_type = col_key.get_type();
6,863,907✔
827
        if (col_type == col_type_LinkList)
6,863,907✔
828
            col_type = col_type_Link;
15,600✔
829
        auto attr = col_key.get_attrs();
6,863,907✔
830
        if (attr.test(col_attr_Collection)) {
6,863,907✔
831
            auto col_ndx = col_key.get_index();
52,866✔
832
            ArrayRef values(m_alloc);
52,866✔
833
            values.set_parent(this, col_ndx.val + s_first_col_index);
52,866✔
834
            values.init_from_parent();
52,866✔
835
            ref_type ref = values.get(ndx);
52,866✔
836

26,721✔
837
            if (ref) {
52,866✔
838
                const Table* origin_table = m_tree_top.get_owning_table();
18,354✔
839
                if (attr.test(col_attr_Dictionary)) {
18,354✔
840
                    if (col_type == col_type_Mixed || col_type == col_type_Link) {
2,649✔
841
                        Obj obj(origin_table->m_own_ref, get_mem(), key, ndx);
354✔
842
                        Dictionary dict(obj, col_key);
354✔
843
                        dict.remove_backlinks(state);
354✔
844
                    }
354✔
845
                }
2,649✔
846
                else if (col_type == col_type_Link) {
15,705✔
847
                    BPlusTree<ObjKey> links(m_alloc);
3,492✔
848
                    links.init_from_ref(ref);
3,492✔
849
                    if (links.size() > 0) {
3,492✔
850
                        do_remove_backlinks(ObjKey(key.value + m_offset), col_key, links.get_all(), state);
1,353✔
851
                    }
1,353✔
852
                }
3,492✔
853
                else if (col_type == col_type_TypedLink) {
12,213✔
UNCOV
854
                    BPlusTree<ObjLink> links(m_alloc);
×
UNCOV
855
                    links.init_from_ref(ref);
×
UNCOV
856
                    for (size_t i = 0; i < links.size(); i++) {
×
UNCOV
857
                        ObjLink link = links.get(i);
×
UNCOV
858
                        auto target_obj = origin_table->get_parent_group()->get_object(link);
×
UNCOV
859
                        ColKey backlink_col_key =
×
UNCOV
860
                            target_obj.get_table()->find_backlink_column(col_key, origin_table->get_key());
×
UNCOV
861
                        target_obj.remove_one_backlink(backlink_col_key, ObjKey(key.value + m_offset));
×
UNCOV
862
                    }
×
UNCOV
863
                }
×
864
                else if (col_type == col_type_Mixed) {
12,213✔
865
                    Obj obj(origin_table->m_own_ref, get_mem(), key, ndx);
216✔
866
                    Lst<Mixed> list(obj, col_key);
216✔
867
                    list.remove_backlinks(state);
216✔
868
                }
216✔
869
                Array::destroy_deep(ref, m_alloc);
18,354✔
870
            }
18,354✔
871

26,721✔
872
            values.erase(ndx);
52,866✔
873

26,721✔
874
            return IteratorControl::AdvanceToNext;
52,866✔
875
        }
52,866✔
876

3,404,496✔
877
        switch (col_type) {
6,811,041✔
878
            case col_type_Int:
6,081,525✔
879
                if (attr.test(col_attr_Nullable)) {
6,081,525✔
880
                    do_erase<ArrayIntNull>(ndx, col_key);
1,290,255✔
881
                }
1,290,255✔
882
                else {
4,791,270✔
883
                    do_erase<ArrayInteger>(ndx, col_key);
4,791,270✔
884
                }
4,791,270✔
885
                break;
6,081,525✔
886
            case col_type_Bool:
42,393✔
887
                do_erase<ArrayBoolNull>(ndx, col_key);
42,393✔
888
                break;
42,393✔
889
            case col_type_Float:
36,489✔
890
                do_erase<ArrayFloatNull>(ndx, col_key);
36,489✔
891
                break;
36,489✔
892
            case col_type_Double:
36,501✔
893
                do_erase<ArrayDoubleNull>(ndx, col_key);
36,501✔
894
                break;
36,501✔
895
            case col_type_String:
123,033✔
896
                do_erase<ArrayString>(ndx, col_key);
123,033✔
897
                break;
123,033✔
898
            case col_type_Binary:
38,772✔
899
                do_erase<ArrayBinary>(ndx, col_key);
38,772✔
900
                break;
38,772✔
901
            case col_type_Mixed:
723✔
902
                do_erase<ArrayMixed>(ndx, col_key);
723✔
903
                break;
723✔
904
            case col_type_Timestamp:
45,642✔
905
                do_erase<ArrayTimestamp>(ndx, col_key);
45,642✔
906
                break;
45,642✔
907
            case col_type_Decimal:
36,279✔
908
                do_erase<ArrayDecimal128>(ndx, col_key);
36,279✔
909
                break;
36,279✔
910
            case col_type_ObjectId:
39,018✔
911
                do_erase<ArrayObjectIdNull>(ndx, col_key);
39,018✔
912
                break;
39,018✔
913
            case col_type_UUID:
60,285✔
914
                do_erase<ArrayUUIDNull>(ndx, col_key);
60,285✔
915
                break;
60,285✔
916
            case col_type_Link:
67,155✔
917
                do_erase_key(ndx, col_key, state);
67,155✔
918
                break;
67,155✔
UNCOV
919
            case col_type_TypedLink:
✔
UNCOV
920
                do_erase<ArrayTypedLink>(ndx, col_key);
×
UNCOV
921
                break;
×
922
            case col_type_BackLink:
203,397✔
923
                if (state.m_mode == CascadeState::Mode::None) {
203,397✔
924
                    do_erase<ArrayBacklink>(ndx, col_key);
176,715✔
925
                }
176,715✔
926
                else {
26,682✔
927
                    // Postpone the deletion of backlink entries or else the
13,320✔
928
                    // checks for if there's any remaining backlinks will
13,320✔
929
                    // check the wrong row for columns which have already
13,320✔
930
                    // had values erased from them.
13,320✔
931
                    backlink_column_keys.push_back(col_key);
26,682✔
932
                }
26,682✔
933
                break;
203,397✔
934
            default:
✔
935
                REALM_ASSERT(false);
×
936
                break;
×
937
        }
6,810,927✔
938
        return IteratorControl::AdvanceToNext;
6,810,927✔
939
    };
6,810,927✔
940
    m_tree_top.m_owner->for_each_and_every_column(erase_in_column);
5,086,737✔
941

2,543,484✔
942
    // Any remaining backlink columns to erase from?
2,543,484✔
943
    for (auto k : backlink_column_keys)
5,086,737✔
944
        do_erase<ArrayBacklink>(ndx, k);
26,682✔
945

2,543,484✔
946
    if (m_keys.is_attached()) {
5,086,737✔
947
        m_keys.erase(ndx);
5,029,752✔
948
    }
5,029,752✔
949
    else {
56,985✔
950
        size_t current_size = get_size_in_compact_form();
56,985✔
951
        if (ndx == current_size - 1) {
56,985✔
952
            // When deleting last, we can still maintain compact form
18,597✔
953
            set(0, RefOrTagged::make_tagged(current_size - 1));
37,134✔
954
        }
37,134✔
955
        else {
19,851✔
956
            ensure_general_form();
19,851✔
957
            m_keys.erase(ndx);
19,851✔
958
        }
19,851✔
959
    }
56,985✔
960

2,543,484✔
961
    return node_size();
5,086,737✔
962
}
5,086,737✔
963

964
void Cluster::nullify_incoming_links(ObjKey key, CascadeState& state)
965
{
4,987,089✔
966
    size_t ndx = get_ndx(key, 0);
4,987,089✔
967
    if (ndx == realm::npos)
4,987,089✔
968
        throw KeyNotFound(util::format("Key '%1' not found in '%2' when nullifying incoming links", key.value,
×
969
                                       get_owning_table()->get_class_name()));
×
970

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

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

93,552✔
999
        return IteratorControl::AdvanceToNext;
187,182✔
1000
    };
187,182✔
1001

2,493,573✔
1002
    m_tree_top.get_owning_table()->for_each_backlink_column(nullify_fwd_links);
4,987,089✔
1003
}
4,987,089✔
1004

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

1024
void Cluster::init_leaf(ColKey col_key, ArrayPayload* leaf) const
1025
{
8,169,402✔
1026
    auto col_ndx = col_key.get_index();
8,169,402✔
1027
    // FIXME: Move this validation into callers.
3,687,549✔
1028
    // Currently, the query subsystem may call with an unvalidated key.
3,687,549✔
1029
    // once fixed, reintroduce the noexcept declaration :-D
3,687,549✔
1030
    if (auto t = m_tree_top.get_owning_table())
8,169,402✔
1031
        t->check_column(col_key);
8,503,305✔
1032
    ref_type ref = to_ref(Array::get(col_ndx.val + 1));
8,169,402✔
1033
    if (leaf->need_spec()) {
8,169,402✔
1034
        m_tree_top.set_spec(*leaf, col_ndx);
164,805✔
1035
    }
164,805✔
1036
    leaf->init_from_ref(ref);
8,169,402✔
1037
    leaf->set_parent(const_cast<Cluster*>(this), col_ndx.val + 1);
8,169,402✔
1038
}
8,169,402✔
1039

1040
void Cluster::add_leaf(ColKey col_key, ref_type ref)
1041
{
×
1042
    auto col_ndx = col_key.get_index();
×
1043
    REALM_ASSERT((col_ndx.val + 1) == size());
×
1044
    Array::insert(col_ndx.val + 1, from_ref(ref));
×
1045
}
×
1046

1047
template <typename ArrayType>
1048
void Cluster::verify(ref_type ref, size_t index, util::Optional<size_t>& sz) const
1049
{
6,957,774✔
1050
    ArrayType arr(get_alloc());
6,957,774✔
1051
    set_spec(arr, ColKey::Idx{unsigned(index) - 1});
6,957,774✔
1052
    arr.set_parent(const_cast<Cluster*>(this), index);
6,957,774✔
1053
    arr.init_from_ref(ref);
6,957,774✔
1054
    arr.verify();
6,957,774✔
1055
    if (sz) {
6,957,774!
1056
        REALM_ASSERT(arr.size() == *sz);
3,215,874!
1057
    }
3,215,874✔
1058
    else {
3,741,900✔
1059
        sz = arr.size();
3,741,900✔
1060
    }
3,741,900✔
1061
}
6,957,774✔
1062
namespace {
1063

1064
template <typename ArrayType>
1065
void verify_list(ArrayRef& arr, size_t sz)
1066
{
1,613,004✔
1067
    for (size_t n = 0; n < sz; n++) {
6,248,136!
1068
        if (ref_type bp_tree_ref = arr.get(n)) {
4,635,132!
1069
            BPlusTree<ArrayType> links(arr.get_alloc());
1,651,692✔
1070
            links.init_from_ref(bp_tree_ref);
1,651,692✔
1071
            links.set_parent(&arr, n);
1,651,692✔
1072
            links.verify();
1,651,692✔
1073
        }
1,651,692✔
1074
    }
4,635,132✔
1075
}
1,613,004✔
1076

1077
template <typename SetType>
1078
void verify_set(ArrayRef& arr, size_t sz)
1079
{
132✔
1080
    for (size_t n = 0; n < sz; ++n) {
306!
1081
        if (ref_type bp_tree_ref = arr.get(n)) {
174!
1082
            BPlusTree<SetType> elements(arr.get_alloc());
174✔
1083
            elements.init_from_ref(bp_tree_ref);
174✔
1084
            elements.set_parent(&arr, n);
174✔
1085
            elements.verify();
174✔
1086

87✔
1087
            // FIXME: Check uniqueness of elements.
87✔
1088
        }
174✔
1089
    }
174✔
1090
}
132✔
1091

1092
} // namespace
1093

1094
void Cluster::verify() const
1095
{
3,778,389✔
1096
#ifdef REALM_DEBUG
3,778,389✔
1097
    util::Optional<size_t> sz;
3,778,389✔
1098

1,911,912✔
1099
    auto verify_column = [this, &sz](ColKey col_key) {
8,583,156✔
1100
        size_t col = col_key.get_index().val + s_first_col_index;
8,583,156✔
1101
        ref_type ref = Array::get_as_ref(col);
8,583,156✔
1102
        auto attr = col_key.get_attrs();
8,583,156✔
1103
        auto col_type = col_key.get_type();
8,583,156✔
1104
        bool nullable = attr.test(col_attr_Nullable);
8,583,156✔
1105

4,310,574✔
1106
        if (attr.test(col_attr_List)) {
8,583,156✔
1107
            ArrayRef arr(get_alloc());
1,613,016✔
1108
            arr.set_parent(const_cast<Cluster*>(this), col);
1,613,016✔
1109
            arr.init_from_ref(ref);
1,613,016✔
1110
            arr.verify();
1,613,016✔
1111
            if (sz) {
1,613,016✔
1112
                REALM_ASSERT(arr.size() == *sz);
1,588,788✔
1113
            }
1,588,788✔
1114
            else {
24,228✔
1115
                sz = arr.size();
24,228✔
1116
            }
24,228✔
1117

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

3,475,113✔
1239
        switch (col_type) {
6,957,756✔
1240
            case col_type_Int:
4,728,165✔
1241
                if (nullable) {
4,728,165✔
1242
                    verify<ArrayIntNull>(ref, col, sz);
795,099✔
1243
                }
795,099✔
1244
                else {
3,933,066✔
1245
                    verify<ArrayInteger>(ref, col, sz);
3,933,066✔
1246
                }
3,933,066✔
1247
                break;
4,728,165✔
1248
            case col_type_Bool:
7,152✔
1249
                verify<ArrayBoolNull>(ref, col, sz);
7,152✔
1250
                break;
7,152✔
1251
            case col_type_Float:
144✔
1252
                verify<ArrayFloatNull>(ref, col, sz);
144✔
1253
                break;
144✔
1254
            case col_type_Double:
156✔
1255
                verify<ArrayDoubleNull>(ref, col, sz);
156✔
1256
                break;
156✔
1257
            case col_type_String:
2,211,039✔
1258
                verify<ArrayString>(ref, col, sz);
2,211,039✔
1259
                break;
2,211,039✔
1260
            case col_type_Binary:
144✔
1261
                verify<ArrayBinary>(ref, col, sz);
144✔
1262
                break;
144✔
1263
            case col_type_Mixed:
60✔
1264
                verify<ArrayMixed>(ref, col, sz);
60✔
1265
                break;
60✔
1266
            case col_type_Timestamp:
6,948✔
1267
                verify<ArrayTimestamp>(ref, col, sz);
6,948✔
1268
                break;
6,948✔
1269
            case col_type_Decimal:
42✔
1270
                verify<ArrayDecimal128>(ref, col, sz);
42✔
1271
                break;
42✔
1272
            case col_type_ObjectId:
42✔
1273
                verify<ArrayObjectIdNull>(ref, col, sz);
42✔
1274
                break;
42✔
1275
            case col_type_UUID:
✔
1276
                verify<ArrayUUIDNull>(ref, col, sz);
×
1277
                break;
×
1278
            case col_type_Link:
1,506✔
1279
                verify<ArrayKey>(ref, col, sz);
1,506✔
1280
                break;
1,506✔
1281
            case col_type_BackLink:
2,376✔
1282
                verify<ArrayBacklink>(ref, col, sz);
2,376✔
1283
                break;
2,376✔
UNCOV
1284
            default:
✔
UNCOV
1285
                break;
×
1286
        }
6,957,771✔
1287
        return IteratorControl::AdvanceToNext;
6,957,771✔
1288
    };
6,957,771✔
1289

1,911,912✔
1290
    m_tree_top.m_owner->for_each_and_every_column(verify_column);
3,778,389✔
1291
#endif
3,778,389✔
1292
}
3,778,389✔
1293

1294
// LCOV_EXCL_START
1295
void Cluster::dump_objects(int64_t key_offset, std::string lead) const
1296
{
×
1297
    std::cout << lead << "leaf - size: " << node_size() << std::endl;
×
1298
    if (!m_keys.is_attached()) {
×
1299
        std::cout << lead << "compact form" << std::endl;
×
1300
    }
×
1301

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

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

1477
void Cluster::remove_backlinks(const Table* origin_table, ObjKey origin_key, ColKey origin_col_key,
1478
                               const std::vector<ObjKey>& keys, CascadeState& state)
1479
{
66,666✔
1480
    TableRef target_table = origin_table->get_opposite_table(origin_col_key);
66,666✔
1481
    ColKey backlink_col_key = origin_table->get_opposite_column(origin_col_key);
66,666✔
1482
    bool strong_links = target_table->is_embedded();
66,666✔
1483

33,330✔
1484
    for (auto key : keys) {
68,226✔
1485
        if (key != null_key) {
68,226✔
1486
            bool is_unres = key.is_unresolved();
68,226✔
1487
            Obj target_obj = is_unres ? target_table->m_tombstones->get(key) : target_table->m_clusters.get(key);
68,211✔
1488
            bool last_removed = target_obj.remove_one_backlink(backlink_col_key, origin_key); // Throws
68,226✔
1489
            if (is_unres) {
68,226✔
1490
                if (last_removed) {
30✔
1491
                    // Check is there are more backlinks
12✔
1492
                    if (!target_obj.has_backlinks(false)) {
24✔
1493
                        // Tombstones can be erased right away - there is no cascading effect
9✔
1494
                        target_table->m_tombstones->erase(key, state);
18✔
1495
                    }
18✔
1496
                }
24✔
1497
            }
30✔
1498
            else {
68,196✔
1499
                state.enqueue_for_cascade(target_obj, strong_links, last_removed);
68,196✔
1500
            }
68,196✔
1501
        }
68,226✔
1502
    }
68,226✔
1503
}
66,666✔
1504

1505
void Cluster::remove_backlinks(const Table* origin_table, ObjKey origin_key, ColKey origin_col_key,
1506
                               const std::vector<ObjLink>& links, CascadeState& state)
1507
{
144✔
1508
    Group* group = origin_table->get_parent_group();
144✔
1509
    TableKey origin_table_key = origin_table->get_key();
144✔
1510

72✔
1511
    for (auto link : links) {
240✔
1512
        if (link) {
240✔
1513
            bool is_unres = link.get_obj_key().is_unresolved();
240✔
1514
            Obj target_obj = group->get_object(link);
240✔
1515
            TableRef target_table = target_obj.get_table();
240✔
1516
            ColKey backlink_col_key = target_table->find_or_add_backlink_column(origin_col_key, origin_table_key);
240✔
1517

120✔
1518
            bool last_removed = target_obj.remove_one_backlink(backlink_col_key, origin_key); // Throws
240✔
1519
            if (is_unres) {
240✔
1520
                if (last_removed) {
6✔
1521
                    // Check is there are more backlinks
3✔
1522
                    if (!target_obj.has_backlinks(false)) {
6✔
1523
                        // Tombstones can be erased right away - there is no cascading effect
3✔
1524
                        target_table->m_tombstones->erase(link.get_obj_key(), state);
6✔
1525
                    }
6✔
1526
                }
6✔
1527
            }
6✔
1528
            else {
234✔
1529
                state.enqueue_for_cascade(target_obj, false, last_removed);
234✔
1530
            }
234✔
1531
        }
240✔
1532
    }
240✔
1533
}
144✔
1534

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