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

realm / realm-core / jorgen.edelbo_337

03 Jul 2024 01:04PM UTC coverage: 90.864% (-0.1%) from 90.984%
jorgen.edelbo_337

Pull #7826

Evergreen

nicola-cab
Merge branch 'master' of github.com:realm/realm-core into next-major
Pull Request #7826: Merge Next major

102968 of 181176 branches covered (56.83%)

3131 of 3738 new or added lines in 54 files covered. (83.76%)

106 existing lines in 23 files now uncovered.

217725 of 239616 relevant lines covered (90.86%)

6844960.2 hits per line

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

74.5
/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)
506,586✔
46
{
1,035,378✔
47
    if (m_values.size() > 1) {
1,035,378✔
48
        // Sort according to ColKey index
49
        std::sort(m_values.begin(), m_values.end(), [](const auto& a, const auto& b) {
53,463✔
50
            return a.col_key.get_index().val < b.col_key.get_index().val;
53,463✔
51
        });
53,463✔
52
    }
17,856✔
53
}
1,035,378✔
54

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

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

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

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

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

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

97

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

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

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

109

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

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

125
    auto column_initialize = [this](ColKey col_key) {
199,449✔
126
        auto col_ndx = col_key.get_index();
199,449✔
127
        while (size() <= col_ndx.val + 1)
398,895✔
128
            add(0);
199,446✔
129
        auto type = col_key.get_type();
199,449✔
130
        auto attr = col_key.get_attrs();
199,449✔
131
        if (attr.test(col_attr_Collection)) {
199,449✔
132
            ArrayRef arr(m_alloc);
6,438✔
133
            arr.create();
6,438✔
134
            arr.set_parent(this, col_ndx.val + s_first_col_index);
6,438✔
135
            arr.update_parent();
6,438✔
136
            return IteratorControl::AdvanceToNext;
6,438✔
137
        }
6,438✔
138
        switch (type) {
193,011✔
139
            case col_type_Int:
116,877✔
140
                if (attr.test(col_attr_Nullable)) {
116,877✔
141
                    do_create<ArrayIntNull>(col_key);
13,128✔
142
                }
13,128✔
143
                else {
103,749✔
144
                    do_create<ArrayInteger>(col_key);
103,749✔
145
                }
103,749✔
146
                break;
116,877✔
147
            case col_type_Bool:
684✔
148
                do_create<ArrayBoolNull>(col_key);
684✔
149
                break;
684✔
150
            case col_type_Float:
1,152✔
151
                do_create<ArrayFloatNull>(col_key);
1,152✔
152
                break;
1,152✔
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,309✔
157
                if (m_tree_top.is_string_enum_type(col_ndx)) {
48,309✔
158
                    do_create<ArrayInteger>(col_key);
5,340✔
159
                }
5,340✔
160
                else {
42,969✔
161
                    do_create<ArrayString>(col_key);
42,969✔
162
                }
42,969✔
163
                break;
48,309✔
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:
4,785✔
172
                do_create<ArrayTimestamp>(col_key);
4,785✔
173
                break;
4,785✔
174
            case col_type_Decimal:
276✔
175
                do_create<ArrayDecimal128>(col_key);
276✔
176
                break;
276✔
177
            case col_type_ObjectId:
2,871✔
178
                do_create<ArrayObjectIdNull>(col_key);
2,871✔
179
                break;
2,871✔
180
            case col_type_UUID:
408✔
181
                do_create<ArrayUUIDNull>(col_key);
408✔
182
                break;
408✔
183
            case col_type_Link:
2,253✔
184
                do_create<ArrayKey>(col_key);
2,253✔
185
                break;
2,253✔
186
            case col_type_TypedLink:
✔
187
                do_create<ArrayTypedLink>(col_key);
×
188
                break;
×
189
            case col_type_BackLink:
3,690✔
190
                do_create<ArrayBacklink>(col_key);
3,690✔
191
                break;
3,690✔
192
            default:
✔
193
                REALM_UNREACHABLE();
194
        }
193,011✔
195
        return IteratorControl::AdvanceToNext;
193,011✔
196
    };
193,011✔
197
    m_tree_top.m_owner->for_each_and_every_column(column_initialize);
102,621✔
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);
102,621✔
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();
102,621✔
205
}
102,621✔
206

207
void Cluster::init(MemRef mem)
208
{
325,871,013✔
209
    Array::init_from_mem(mem);
325,871,013✔
210
    auto rot = Array::get_as_ref_or_tagged(0);
325,871,013✔
211
    if (rot.is_tagged()) {
325,871,013✔
212
        m_keys.detach();
261,113,274✔
213
    }
261,113,274✔
214
    else {
64,757,739✔
215
        m_keys.init_from_ref(rot.get_as_ref());
64,757,739✔
216
    }
64,757,739✔
217
}
325,871,013✔
218

219
void Cluster::update_from_parent() noexcept
220
{
1,002,024✔
221
    Array::update_from_parent();
1,002,024✔
222
    auto rot = Array::get_as_ref_or_tagged(0);
1,002,024✔
223
    if (!rot.is_tagged()) {
1,002,024✔
224
        m_keys.update_from_parent();
32,526✔
225
    }
32,526✔
226
}
1,002,024✔
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,944,617✔
244
    auto rot = Array::get_as_ref_or_tagged(header, s_key_ref_or_size_index);
71,944,617✔
245
    if (rot.is_tagged()) {
71,944,617✔
246
        return size_t(rot.get_as_int());
70,607,352✔
247
    }
70,607,352✔
248
    else {
1,337,265✔
249
        return Array::get_size_from_header(alloc.translate(rot.get_as_ref()));
1,337,265✔
250
    }
1,337,265✔
251
}
71,944,617✔
252

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

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

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

269
    T arr(m_alloc);
35,334,498✔
270
    auto col_ndx = col.get_index();
35,334,498✔
271
    arr.set_parent(this, col_ndx.val + s_first_col_index);
35,334,498✔
272
    set_spec<T>(arr, col_ndx);
35,334,498✔
273
    arr.init_from_parent();
35,334,498✔
274
    if (init_val.is_null()) {
35,334,498✔
275
        arr.insert(ndx, T::default_value(nullable));
34,239,102✔
276
    }
34,239,102✔
277
    else {
1,095,396✔
278
        arr.insert(ndx, init_val.get<U>());
1,095,396✔
279
    }
1,095,396✔
280
}
35,334,498✔
281

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

291
    // Insert backlink if link is not null
292
    if (target_key) {
278,370✔
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
}
278,370✔
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
{
25,115,736✔
348
    // Ensure the cluster array is big enough to hold 64 bit values.
349
    copy_on_write(m_size * 8);
25,115,736✔
350

351
    if (m_keys.is_attached()) {
25,115,736✔
352
        m_keys.insert(ndx, row_key.value);
1,582,356✔
353
    }
1,582,356✔
354
    else {
23,533,380✔
355
        Array::set(s_key_ref_or_size_index, Array::get(s_key_ref_or_size_index) + 2); // Increments size by 1
23,533,380✔
356
    }
23,533,380✔
357

358
    auto val = init_values.begin();
25,115,736✔
359
    auto insert_in_column = [&](ColKey col_key) {
37,625,706✔
360
        auto col_ndx = col_key.get_index();
37,625,706✔
361
        auto attr = col_key.get_attrs();
37,625,706✔
362
        Mixed init_value;
37,625,706✔
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) {
37,625,706✔
365
            init_value = val->value;
932,430✔
366
            ++val;
932,430✔
367
        }
932,430✔
368

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

379
        bool nullable = attr.test(col_attr_Nullable);
36,874,389✔
380
        ObjKey obj_key(int64_t(row_key.value + get_offset()));
36,874,389✔
381
        switch (type) {
36,874,389✔
382
            case col_type_Int:
27,649,539✔
383
                if (attr.test(col_attr_Nullable)) {
27,649,539✔
384
                    do_insert_row<ArrayIntNull>(ndx, col_key, init_value, nullable);
2,749,125✔
385
                }
2,749,125✔
386
                else {
24,900,414✔
387
                    do_insert_row<ArrayInteger>(ndx, col_key, init_value, nullable);
24,900,414✔
388
                }
24,900,414✔
389
                break;
27,649,539✔
390
            case col_type_Bool:
186,849✔
391
                do_insert_row<ArrayBoolNull>(ndx, col_key, init_value, nullable);
186,849✔
392
                break;
186,849✔
393
            case col_type_Float:
336,711✔
394
                do_insert_row<ArrayFloatNull>(ndx, col_key, init_value, nullable);
336,711✔
395
                break;
336,711✔
396
            case col_type_Double:
1,493,307✔
397
                do_insert_row<ArrayDoubleNull>(ndx, col_key, init_value, nullable);
1,493,307✔
398
                break;
1,493,307✔
399
            case col_type_String:
3,102,540✔
400
                do_insert_row<ArrayString>(ndx, col_key, init_value, nullable);
3,102,540✔
401
                break;
3,102,540✔
402
            case col_type_Binary:
1,364,172✔
403
                do_insert_row<ArrayBinary>(ndx, col_key, init_value, nullable);
1,364,172✔
404
                break;
1,364,172✔
405
            case col_type_Mixed: {
166,335✔
406
                do_insert_mixed(ndx, col_key, init_value, obj_key);
166,335✔
407
                break;
166,335✔
408
            }
×
409
            case col_type_Timestamp:
825,240✔
410
                do_insert_row<ArrayTimestamp>(ndx, col_key, init_value, nullable);
825,240✔
411
                break;
825,240✔
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:
173,478✔
416
                do_insert_row<ArrayObjectIdNull>(ndx, col_key, init_value, nullable);
173,478✔
417
                break;
173,478✔
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:
278,370✔
422
                do_insert_key(ndx, col_key, init_value, obj_key);
278,370✔
423
                break;
278,370✔
424
            case col_type_TypedLink:
✔
425
                do_insert_link(ndx, col_key, init_value, obj_key);
×
426
                break;
×
427
            case col_type_BackLink: {
1,149,261✔
428
                ArrayBacklink arr(m_alloc);
1,149,261✔
429
                arr.set_parent(this, col_ndx.val + s_first_col_index);
1,149,261✔
430
                arr.init_from_parent();
1,149,261✔
431
                arr.insert(ndx, 0);
1,149,261✔
432
                break;
1,149,261✔
433
            }
×
434
            default:
✔
435
                REALM_ASSERT(false);
×
436
                break;
×
437
        }
36,874,389✔
438
        return IteratorControl::AdvanceToNext;
36,745,023✔
439
    };
36,874,389✔
440
    m_tree_top.m_owner->for_each_and_every_column(insert_in_column);
25,115,736✔
441
}
25,115,736✔
442

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

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

455
    src.move(dst, ndx);
23,814✔
456
}
23,814✔
457

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

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

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

471
        switch (type) {
23,520✔
472
            case col_type_Int:
20,898✔
473
                if (attr.test(col_attr_Nullable)) {
20,898✔
474
                    do_move<ArrayIntNull>(ndx, col_key, new_leaf);
9,447✔
475
                }
9,447✔
476
                else {
11,451✔
477
                    do_move<ArrayInteger>(ndx, col_key, new_leaf);
11,451✔
478
                }
11,451✔
479
                break;
20,898✔
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,520✔
527
        return IteratorControl::AdvanceToNext;
23,520✔
528
    };
23,520✔
529
    m_tree_top.m_owner->for_each_and_every_column(move_from_column);
12,477✔
530
    for (size_t i = ndx; i < m_keys.size(); i++) {
1,328,919✔
531
        new_leaf->m_keys.add(m_keys.get(i) - offset);
1,316,442✔
532
    }
1,316,442✔
533
    m_keys.truncate(ndx);
12,477✔
534
}
12,477✔
535

536
Cluster::~Cluster() {}
329,175,384✔
537

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

546
void Cluster::ensure_general_form()
547
{
64,767✔
548
    if (!m_keys.is_attached()) {
64,767✔
549
        size_t current_size = get_size_in_compact_form();
49,491✔
550
        m_keys.create(current_size, 255);
49,491✔
551
        m_keys.update_parent();
49,491✔
552
        for (size_t i = 0; i < current_size; i++) {
5,798,364✔
553
            m_keys.set(i, i);
5,748,873✔
554
        }
5,748,873✔
555
    }
49,491✔
556
}
64,767✔
557

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

563
    T arr(m_alloc);
611,394✔
564
    arr.create();
611,394✔
565
    auto val = T::default_value(nullable);
611,394✔
566
    for (size_t i = 0; i < sz; i++) {
6,843,447✔
567
        arr.add(val);
6,232,053✔
568
    }
6,232,053✔
569
    auto col_ndx = col_key.get_index();
611,394✔
570
    unsigned ndx = col_ndx.val + s_first_col_index;
611,394✔
571

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

576
    if (ndx == size())
611,394✔
577
        Array::insert(ndx, from_ref(arr.get_ref()));
611,307✔
578
    else
87✔
579
        Array::set(ndx, from_ref(arr.get_ref()));
87✔
580
}
611,394✔
581

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

589
        ArrayRef arr(m_alloc);
195,267✔
590
        arr.create(sz);
195,267✔
591
        auto col_ndx = col_key.get_index();
195,267✔
592
        unsigned idx = col_ndx.val + s_first_col_index;
195,267✔
593
        if (idx == size())
195,267✔
594
            Array::insert(idx, from_ref(arr.get_ref()));
195,267✔
595
        else
×
596
            Array::set(idx, from_ref(arr.get_ref()));
×
597
        return;
195,267✔
598
    }
195,267✔
599
    bool nullable = attr.test(col_attr_Nullable);
611,394✔
600
    switch (type) {
611,394✔
601
        case col_type_Int:
274,884✔
602
            if (nullable) {
274,884✔
603
                do_insert_column<ArrayIntNull>(col_key, nullable);
32,781✔
604
            }
32,781✔
605
            else {
242,103✔
606
                do_insert_column<ArrayInteger>(col_key, nullable);
242,103✔
607
            }
242,103✔
608
            break;
274,884✔
609
        case col_type_Bool:
5,481✔
610
            do_insert_column<ArrayBoolNull>(col_key, nullable);
5,481✔
611
            break;
5,481✔
612
        case col_type_Float:
6,438✔
613
            do_insert_column<ArrayFloatNull>(col_key, nullable);
6,438✔
614
            break;
6,438✔
615
        case col_type_Double:
7,083✔
616
            do_insert_column<ArrayDoubleNull>(col_key, nullable);
7,083✔
617
            break;
7,083✔
618
        case col_type_String:
101,670✔
619
            do_insert_column<ArrayString>(col_key, nullable);
101,670✔
620
            break;
101,670✔
621
        case col_type_Binary:
6,891✔
622
            do_insert_column<ArrayBinary>(col_key, nullable);
6,891✔
623
            break;
6,891✔
624
        case col_type_Mixed:
9,114✔
625
            do_insert_column<ArrayMixed>(col_key, nullable);
9,114✔
626
            break;
9,114✔
627
        case col_type_Timestamp:
23,631✔
628
            do_insert_column<ArrayTimestamp>(col_key, nullable);
23,631✔
629
            break;
23,631✔
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:
50,577✔
634
            do_insert_column<ArrayObjectIdNull>(col_key, nullable);
50,577✔
635
            break;
50,577✔
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,098✔
640
            do_insert_column<ArrayKey>(col_key, nullable);
31,098✔
641
            break;
31,098✔
642
        case col_type_TypedLink:
✔
643
            do_insert_column<ArrayTypedLink>(col_key, nullable);
×
644
            break;
×
645
        case col_type_BackLink:
83,889✔
646
            do_insert_column<ArrayBacklink>(col_key, nullable);
83,889✔
647
            break;
83,889✔
648
        default:
✔
649
            REALM_UNREACHABLE();
650
            break;
×
651
    }
611,394✔
652
}
611,394✔
653

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

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

675
    auto on_error = [&] {
25,135,698✔
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()) {
25,135,698✔
681
        sz = m_keys.size();
1,570,959✔
682
        ndx = m_keys.lower_bound(row_key.value);
1,570,959✔
683
        if (ndx < sz) {
1,570,959✔
684
            current_key_value = m_keys.get(ndx);
616,563✔
685
            if (row_key.value == uint64_t(current_key_value)) {
616,563✔
686
                on_error();
12✔
687
            }
12✔
688
        }
616,563✔
689
    }
1,570,959✔
690
    else {
23,564,739✔
691
        sz = size_t(Array::get(s_key_ref_or_size_index)) >> 1; // Size is stored as tagged integer
23,564,739✔
692
        if (row_key.value < sz) {
23,564,739✔
693
            on_error();
×
694
        }
×
695
        // Key value is bigger than all other values, should be put last
696
        ndx = sz;
23,564,739✔
697
        if (row_key.value > sz && sz < cluster_node_size) {
23,564,739✔
698
            ensure_general_form();
17,022✔
699
        }
17,022✔
700
    }
23,564,739✔
701

702
    REALM_ASSERT_DEBUG(sz <= cluster_node_size);
25,135,698✔
703
    if (REALM_LIKELY(sz < cluster_node_size)) {
25,135,698✔
704
        insert_row(ndx, row_key, init_values); // Throws
25,015,758✔
705
        state.mem = get_mem();
25,015,758✔
706
        state.index = ndx;
25,015,758✔
707
    }
25,015,758✔
708
    else {
119,940✔
709
        // Split leaf node
710
        Cluster new_leaf(0, m_alloc, m_tree_top);
119,940✔
711
        new_leaf.create();
119,940✔
712
        if (ndx == sz) {
122,628✔
713
            new_leaf.insert_row(0, RowKey(0), init_values); // Throws
91,845✔
714
            state.split_key = int64_t(row_key.value);
91,845✔
715
            state.mem = new_leaf.get_mem();
91,845✔
716
            state.index = 0;
91,845✔
717
        }
91,845✔
718
        else {
2,147,514,430✔
719
            // Current cluster must be in general form to get here
720
            REALM_ASSERT_DEBUG(m_keys.is_attached());
2,147,514,430✔
721
            new_leaf.ensure_general_form();
2,147,514,430✔
722
            move(ndx, &new_leaf, current_key_value);
2,147,514,430✔
723
            insert_row(ndx, row_key, init_values); // Throws
2,147,514,430✔
724
            state.mem = get_mem();
2,147,514,430✔
725
            state.split_key = current_key_value;
2,147,514,430✔
726
            state.index = ndx;
2,147,514,430✔
727
        }
2,147,514,430✔
728
        ret = new_leaf.get_ref();
119,940✔
729
    }
119,940✔
730

731
    return ret;
25,135,698✔
732
}
25,135,698✔
733

734
bool Cluster::try_get(RowKey k, ClusterNode::State& state) const noexcept
735
{
275,460,441✔
736
    state.mem = get_mem();
275,460,441✔
737
    if (m_keys.is_attached()) {
275,460,441✔
738
        state.index = m_keys.lower_bound(k.value);
51,017,034✔
739
        return state.index != m_keys.size() && m_keys.get(state.index) == k.value;
51,017,034✔
740
    }
51,017,034✔
741
    else {
224,443,407✔
742
        if (k.value < uint64_t(Array::get(s_key_ref_or_size_index) >> 1)) {
224,443,407✔
743
            state.index = size_t(k.value);
205,713,297✔
744
            return true;
205,713,297✔
745
        }
205,713,297✔
746
    }
224,443,407✔
747
    return false;
18,730,110✔
748
}
275,460,441✔
749

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

757
template <class T>
758
inline void Cluster::do_erase(size_t ndx, ColKey col_key)
759
{
6,927,015✔
760
    auto col_ndx = col_key.get_index();
6,927,015✔
761
    T values(m_alloc);
6,927,015✔
762
    values.set_parent(this, col_ndx.val + s_first_col_index);
6,927,015✔
763
    set_spec<T>(values, col_ndx);
6,927,015✔
764
    values.init_from_parent();
6,927,015✔
765
    if constexpr (std::is_same_v<T, ArrayTypedLink>) {
6,927,015✔
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);
6,927,015✔
778
}
6,927,015✔
779

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

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

789
    Mixed value = values.get(ndx);
145,437✔
790
    if (value.is_type(type_TypedLink)) {
145,437✔
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,437✔
796
        Obj obj(origin_table->m_own_ref, get_mem(), get_real_key(ndx), ndx);
72,042✔
797
        Lst<Mixed> list(obj, col_key);
72,042✔
798
        list.remove_backlinks(state);
72,042✔
799
    }
72,042✔
800
    if (value.is_type(type_Dictionary)) {
145,437✔
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,437✔
806
}
145,437✔
807

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

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

822
size_t Cluster::get_ndx(RowKey k, size_t ndx) const noexcept
823
{
10,502,769✔
824
    size_t index;
10,502,769✔
825
    if (m_keys.is_attached()) {
10,502,769✔
826
        index = m_keys.lower_bound(k.value);
10,385,757✔
827
        if (index == m_keys.size() || m_keys.get(index) != k.value) {
10,386,285✔
828
            return realm::npos;
18✔
829
        }
18✔
830
    }
10,385,757✔
831
    else {
117,012✔
832
        index = size_t(k.value);
117,012✔
833
        if (index >= get_as_ref_or_tagged(s_key_ref_or_size_index).get_as_int()) {
117,012✔
834
            return realm::npos;
×
835
        }
×
836
    }
117,012✔
837
    return index + ndx;
10,502,751✔
838
}
10,502,769✔
839

840
size_t Cluster::erase(RowKey row_key, CascadeState& state)
841
{
5,282,238✔
842
    size_t ndx = get_ndx(row_key, 0);
5,282,238✔
843
    if (ndx == realm::npos)
5,282,238✔
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,282,220✔
848
    std::vector<ColKey> backlink_column_keys;
5,282,220✔
849

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

860
            if (ref) {
104,157✔
861
                const Table* origin_table = m_tree_top.get_owning_table();
83,529✔
862
                if (attr.test(col_attr_Dictionary)) {
83,529✔
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) {
44,874✔
870
                    BPlusTree<ObjKey> links(m_alloc);
3,933✔
871
                    links.init_from_ref(ref);
3,933✔
872
                    if (links.size() > 0) {
3,933✔
873
                        do_remove_backlinks(real_key, col_key, links.get_all(), state);
999✔
874
                    }
999✔
875
                }
3,933✔
876
                else if (col_type == col_type_TypedLink) {
40,941✔
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,941✔
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);
83,529✔
893
            }
83,529✔
894

895
            values.erase(ndx);
104,157✔
896

897
            return IteratorControl::AdvanceToNext;
104,157✔
898
        }
104,157✔
899

900
        switch (col_type) {
7,164,309✔
901
            case col_type_Int:
5,983,953✔
902
                if (attr.test(col_attr_Nullable)) {
5,983,953✔
903
                    do_erase<ArrayIntNull>(ndx, col_key);
1,275,630✔
904
                }
1,275,630✔
905
                else {
4,708,323✔
906
                    do_erase<ArrayInteger>(ndx, col_key);
4,708,323✔
907
                }
4,708,323✔
908
                break;
5,983,953✔
909
            case col_type_Bool:
42,318✔
910
                do_erase<ArrayBoolNull>(ndx, col_key);
42,318✔
911
                break;
42,318✔
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:
357,663✔
919
                do_erase<ArrayString>(ndx, col_key);
357,663✔
920
                break;
357,663✔
921
            case col_type_Binary:
39,597✔
922
                do_erase<ArrayBinary>(ndx, col_key);
39,597✔
923
                break;
39,597✔
924
            case col_type_Mixed:
145,437✔
925
                do_erase_mixed(ndx, col_key, state);
145,437✔
926
                break;
145,437✔
927
            case col_type_Timestamp:
45,360✔
928
                do_erase<ArrayTimestamp>(ndx, col_key);
45,360✔
929
                break;
45,360✔
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:
38,745✔
934
                do_erase<ArrayObjectIdNull>(ndx, col_key);
38,745✔
935
                break;
38,745✔
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,133✔
940
                do_erase_key(ndx, col_key, state);
92,133✔
941
                break;
92,133✔
942
            case col_type_TypedLink:
✔
943
                do_erase<ArrayTypedLink>(ndx, col_key);
×
944
                break;
×
945
            case col_type_BackLink:
249,825✔
946
                if (state.m_mode == CascadeState::Mode::None) {
249,825✔
947
                    do_erase<ArrayBacklink>(ndx, col_key);
188,607✔
948
                }
188,607✔
949
                else {
61,218✔
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);
61,218✔
955
                }
61,218✔
956
                break;
249,825✔
957
            default:
✔
958
                REALM_ASSERT(false);
×
959
                break;
×
960
        }
7,164,309✔
961
        return IteratorControl::AdvanceToNext;
7,164,126✔
962
    };
7,164,309✔
963
    m_tree_top.m_owner->for_each_and_every_column(erase_in_column);
5,282,220✔
964

965
    // Any remaining backlink columns to erase from?
966
    for (auto k : backlink_column_keys)
5,282,220✔
967
        do_erase<ArrayBacklink>(ndx, k);
61,221✔
968

969
    if (m_keys.is_attached()) {
5,282,220✔
970
        m_keys.erase(ndx);
5,240,637✔
971
    }
5,240,637✔
972
    else {
41,583✔
973
        size_t current_size = get_size_in_compact_form();
41,583✔
974
        if (ndx == current_size - 1) {
41,583✔
975
            // When deleting last, we can still maintain compact form
976
            set(0, RefOrTagged::make_tagged(current_size - 1));
26,322✔
977
        }
26,322✔
978
        else {
15,261✔
979
            ensure_general_form();
15,261✔
980
            m_keys.erase(ndx);
15,261✔
981
        }
15,261✔
982
    }
41,583✔
983

984
    return node_size();
5,282,220✔
985
}
5,282,238✔
986

987
void Cluster::nullify_incoming_links(RowKey key, CascadeState& state)
988
{
5,183,820✔
989
    size_t ndx = get_ndx(key, 0);
5,183,820✔
990
    if (ndx == realm::npos)
5,183,820✔
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,183,820✔
1006
        ColKey::Idx leaf_ndx = col_key.get_index();
232,359✔
1007
        auto type = col_key.get_type();
232,359✔
1008
        REALM_ASSERT(type == col_type_BackLink);
232,359✔
1009
        ArrayBacklink values(m_alloc);
232,359✔
1010
        values.set_parent(this, leaf_ndx.val + s_first_col_index);
232,359✔
1011
        values.init_from_parent();
232,359✔
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();
232,359✔
1020
        values.nullify_fwd_links(ndx, state);
232,359✔
1021

1022
        return IteratorControl::AdvanceToNext;
232,359✔
1023
    };
232,359✔
1024

1025
    m_tree_top.get_owning_table()->for_each_backlink_column(nullify_fwd_links);
5,183,820✔
1026
}
5,183,820✔
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,941✔
1038
        auto v = values.get(i);
117,852✔
1039
        size_t pos = keys.lower_bound(v);
117,852✔
1040
        REALM_ASSERT_3(pos, !=, keys.size());
117,852✔
1041
        indexes.add(pos);
117,852✔
1042
    }
117,852✔
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,570,847✔
1049
    auto col_ndx = col_key.get_index();
8,570,847✔
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,570,847✔
1054
        t->check_column(col_key);
8,345,190✔
1055
    ref_type ref = to_ref(Array::get(col_ndx.val + 1));
8,570,847✔
1056
    if (leaf->need_spec()) {
8,570,847✔
1057
        m_tree_top.set_spec(*leaf, col_ndx);
142,197✔
1058
    }
142,197✔
1059
    leaf->init_from_ref(ref);
8,570,847✔
1060
    leaf->set_parent(const_cast<Cluster*>(this), col_ndx.val + 1);
8,570,847✔
1061
}
8,570,847✔
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
{
546,222✔
1073
    ArrayType arr(get_alloc());
546,222✔
1074
    set_spec(arr, ColKey::Idx{unsigned(index) - 1});
546,222✔
1075
    arr.set_parent(const_cast<Cluster*>(this), index);
546,222✔
1076
    arr.init_from_ref(ref);
546,222✔
1077
    arr.verify();
546,222✔
1078
    if (sz) {
546,222✔
1079
        REALM_ASSERT(arr.size() == *sz);
199,452!
1080
    }
199,452✔
1081
    else {
346,770✔
1082
        sz = arr.size();
346,770✔
1083
    }
346,770✔
1084
}
546,222✔
1085
namespace {
1086

1087
template <typename ArrayType>
1088
void verify_list(ArrayRef& arr, size_t sz)
1089
{
102,960✔
1090
    for (size_t n = 0; n < sz; n++) {
373,515!
1091
        if (ref_type bp_tree_ref = arr.get(n)) {
270,555!
1092
            BPlusTree<ArrayType> links(arr.get_alloc());
121,155✔
1093
            links.init_from_ref(bp_tree_ref);
121,155✔
1094
            links.set_parent(&arr, n);
121,155✔
1095
            links.verify();
121,155✔
1096
        }
121,155✔
1097
    }
270,555✔
1098
}
102,960✔
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
{
383,403✔
1119
#ifdef REALM_DEBUG
383,403✔
1120
    util::Optional<size_t> sz;
383,403✔
1121

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

1129
        if (attr.test(col_attr_List)) {
661,632✔
1130
            ArrayRef arr(get_alloc());
102,978✔
1131
            arr.set_parent(const_cast<Cluster*>(this), col);
102,978✔
1132
            arr.init_from_ref(ref);
102,978✔
1133
            arr.verify();
102,978✔
1134
            if (sz) {
102,978✔
1135
                REALM_ASSERT(arr.size() == *sz);
78,678✔
1136
            }
78,678✔
1137
            else {
24,300✔
1138
                sz = arr.size();
24,300✔
1139
            }
24,300✔
1140

1141
            switch (col_type) {
102,978✔
1142
                case col_type_Int:
41,352✔
1143
                    if (nullable) {
41,352✔
1144
                        verify_list<util::Optional<int64_t>>(arr, *sz);
×
1145
                    }
×
1146
                    else {
41,352✔
1147
                        verify_list<int64_t>(arr, *sz);
41,352✔
1148
                    }
41,352✔
1149
                    break;
41,352✔
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:
36,516✔
1160
                    verify_list<String>(arr, *sz);
36,516✔
1161
                    break;
36,516✔
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,086✔
1178
                    verify_list<ObjKey>(arr, *sz);
1,086✔
1179
                    break;
1,086✔
1180
                default:
18✔
1181
                    // FIXME: Nullable primitives
1182
                    break;
18✔
1183
            }
102,978✔
1184
            return IteratorControl::AdvanceToNext;
102,978✔
1185
        }
102,978✔
1186
        else if (attr.test(col_attr_Dictionary)) {
558,654✔
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)) {
546,486✔
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) {
546,216✔
1263
            case col_type_Int:
317,460✔
1264
                if (nullable) {
317,460✔
1265
                    verify<ArrayIntNull>(ref, col, sz);
30,426✔
1266
                }
30,426✔
1267
                else {
287,034✔
1268
                    verify<ArrayInteger>(ref, col, sz);
287,034✔
1269
                }
287,034✔
1270
                break;
317,460✔
1271
            case col_type_Bool:
7,158✔
1272
                verify<ArrayBoolNull>(ref, col, sz);
7,158✔
1273
                break;
7,158✔
1274
            case col_type_Float:
150✔
1275
                verify<ArrayFloatNull>(ref, col, sz);
150✔
1276
                break;
150✔
1277
            case col_type_Double:
156✔
1278
                verify<ArrayDoubleNull>(ref, col, sz);
156✔
1279
                break;
156✔
1280
            case col_type_String:
157,644✔
1281
                verify<ArrayString>(ref, col, sz);
157,644✔
1282
                break;
157,644✔
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,724✔
1287
                verify<ArrayMixed>(ref, col, sz);
2,724✔
1288
                break;
2,724✔
1289
            case col_type_Timestamp:
6,948✔
1290
                verify<ArrayTimestamp>(ref, col, sz);
6,948✔
1291
                break;
6,948✔
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,557✔
1302
                verify<ArrayKey>(ref, col, sz);
1,557✔
1303
                break;
1,557✔
1304
            case col_type_BackLink:
2,469✔
1305
                verify<ArrayBacklink>(ref, col, sz);
2,469✔
1306
                break;
2,469✔
1307
            default:
✔
1308
                break;
×
1309
        }
546,216✔
1310
        return IteratorControl::AdvanceToNext;
546,216✔
1311
    };
546,216✔
1312

1313
    m_tree_top.m_owner->for_each_and_every_column(verify_column);
383,403✔
1314
#endif
383,403✔
1315
}
383,403✔
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,125✔
1504
    TableRef target_table = origin_table->get_opposite_table(origin_col_key);
91,125✔
1505
    ColKey backlink_col_key = origin_table->get_opposite_column(origin_col_key);
91,125✔
1506
    bool strong_links = target_table->is_embedded();
91,125✔
1507

1508
    for (auto key : keys) {
92,775✔
1509
        if (key != null_key) {
92,775✔
1510
            bool is_unres = key.is_unresolved();
92,775✔
1511
            Obj target_obj = is_unres ? target_table->m_tombstones->get(key) : target_table->m_clusters.get(key);
92,775✔
1512
            bool last_removed = target_obj.remove_one_backlink(backlink_col_key, origin_key); // Throws
92,775✔
1513
            if (is_unres) {
92,775✔
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 {
92,745✔
1523
                state.enqueue_for_cascade(target_obj, strong_links, last_removed);
92,745✔
1524
            }
92,745✔
1525
        }
92,775✔
1526
    }
92,775✔
1527
}
91,125✔
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 {
1560

1561
template <typename Fn>
1562
static void switch_on_type(ColKey ck, Fn&& fn)
1563
{
1,576,416✔
1564
    bool is_optional = ck.is_nullable();
1,576,416✔
1565
    auto type = ck.get_type();
1,576,416✔
1566
    switch (type) {
1,576,416✔
1567
        case col_type_Int:
811,194✔
1568
            return is_optional ? fn((util::Optional<int64_t>*)0) : fn((int64_t*)0);
811,194✔
1569
        case col_type_Bool:
9,321✔
1570
            return is_optional ? fn((util::Optional<bool>*)0) : fn((bool*)0);
9,321✔
1571
        case col_type_Float:
9,519✔
1572
            return is_optional ? fn((util::Optional<float>*)0) : fn((float*)0);
9,519✔
1573
        case col_type_Double:
13,527✔
1574
            return is_optional ? fn((util::Optional<double>*)0) : fn((double*)0);
13,527✔
1575
        case col_type_String:
266,223✔
1576
            return fn((StringData*)0);
266,223✔
1577
        case col_type_Binary:
247,986✔
1578
            return fn((BinaryData*)0);
247,986✔
1579
        case col_type_Timestamp:
39,345✔
1580
            return fn((Timestamp*)0);
39,345✔
1581
        case col_type_Link:
56,187✔
1582
            return fn((ObjKey*)0);
56,187✔
1583
        case col_type_ObjectId:
86,886✔
1584
            return is_optional ? fn((util::Optional<ObjectId>*)0) : fn((ObjectId*)0);
86,886✔
1585
        case col_type_Decimal:
8,559✔
1586
            return fn((Decimal128*)0);
8,559✔
1587
        case col_type_UUID:
9,339✔
1588
            return is_optional ? fn((util::Optional<UUID>*)0) : fn((UUID*)0);
9,339✔
1589
        case col_type_Mixed:
18,519✔
1590
            return fn((Mixed*)0);
18,519✔
NEW
1591
        default:
✔
NEW
1592
            REALM_COMPILER_HINT_UNREACHABLE();
×
1593
    }
1,576,416✔
1594
}
1,576,416✔
1595

1596
} // namespace
1597

1598
ref_type Cluster::typed_write(ref_type ref, _impl::ArrayWriterBase& out) const
1599
{
1,068,069✔
1600
    REALM_ASSERT_DEBUG(ref == get_mem().get_ref());
1,068,069✔
1601
    bool only_modified = out.only_modified;
1,068,069✔
1602
    if (only_modified && m_alloc.is_read_only(ref))
1,068,069✔
1603
        return ref;
963✔
1604
    REALM_ASSERT_DEBUG(!get_is_inner_bptree_node_from_header(get_header()));
1,067,106✔
1605
    REALM_ASSERT_DEBUG(!get_context_flag_from_header(get_header()));
1,067,106✔
1606
    TempArray written_cluster(size());
1,067,106✔
1607
    for (size_t j = 0; j < size(); ++j) {
4,670,550✔
1608
        RefOrTagged leaf_rot = get_as_ref_or_tagged(j);
3,603,375✔
1609
        // Handle nulls
1610
        if (!leaf_rot.is_ref() || !leaf_rot.get_as_ref()) {
3,603,375✔
1611
            written_cluster.set(j, leaf_rot);
1,007,076✔
1612
            continue;
1,007,076✔
1613
        }
1,007,076✔
1614
        // prune subtrees which should not be written:
1615
        if (only_modified && m_alloc.is_read_only(leaf_rot.get_as_ref())) {
2,596,299✔
1616
            written_cluster.set(j, leaf_rot);
345,645✔
1617
            continue;
345,645✔
1618
        }
345,645✔
1619
        // from here: this leaf exists and needs to be written.
1620
        ref = leaf_rot.get_as_ref();
2,250,654✔
1621
        ref_type new_ref = ref;
2,250,654✔
1622
        if (j == 0) {
2,250,654✔
1623
            // Keys  (ArrayUnsigned me thinks, so don't compress)
1624
            Array leaf(m_alloc);
48,333✔
1625
            leaf.init_from_ref(ref);
48,333✔
1626
            new_ref = leaf.write(out, false, only_modified, false);
48,333✔
1627
        }
48,333✔
1628
        else {
2,202,321✔
1629
            // Columns
1630
            auto col_key = out.table->m_leaf_ndx2colkey[j - 1];
2,202,321✔
1631
            auto col_type = col_key.get_type();
2,202,321✔
1632
            if (col_key.is_collection()) {
2,202,321✔
1633
                ArrayRef arr_ref(m_alloc);
453,129✔
1634
                arr_ref.init_from_ref(ref);
453,129✔
1635
                auto sz = arr_ref.size();
453,129✔
1636
                TempArray written_ref_leaf(sz);
453,129✔
1637

1638
                for (size_t k = 0; k < sz; k++) {
4,805,466✔
1639
                    ref_type new_sub_ref = 0;
4,352,268✔
1640
                    // Now we have to find out if the nested collection is a
1641
                    // dictionary or a list. If the top array has a size of 2
1642
                    // and it is not a BplusTree inner node, then it is a dictionary
1643
                    if (auto sub_ref = arr_ref.get(k)) {
4,352,268✔
1644
                        if (col_key.is_dictionary()) {
2,605,596✔
1645
                            new_sub_ref = Dictionary::typed_write(sub_ref, out, m_alloc);
43,812✔
1646
                        }
43,812✔
1647
                        else {
2,561,784✔
1648
                            // List or set - Can be handled the same way
1649
                            // For some reason, switch_on_type() would not compile on Windows
1650
                            // switch_on_type(col_key, [&](auto t) {
1651
                            //     using U = std::decay_t<decltype(*t)>;
1652
                            //     new_sub_ref = BPlusTree<U>::typed_write(sub_ref, out, m_alloc);
1653
                            // });
1654
                            switch (col_type) {
2,561,784✔
1655
                                case col_type_Int:
27,888✔
1656
                                    new_sub_ref = BPlusTree<int64_t>::typed_write(sub_ref, out, m_alloc);
27,888✔
1657
                                    break;
27,888✔
1658
                                case col_type_Bool:
2,118✔
1659
                                    new_sub_ref = BPlusTree<bool>::typed_write(sub_ref, out, m_alloc);
2,118✔
1660
                                    break;
2,118✔
1661
                                case col_type_Float:
2,208✔
1662
                                    new_sub_ref = BPlusTree<float>::typed_write(sub_ref, out, m_alloc);
2,208✔
1663
                                    break;
2,208✔
1664
                                case col_type_Double:
2,400✔
1665
                                    new_sub_ref = BPlusTree<double>::typed_write(sub_ref, out, m_alloc);
2,400✔
1666
                                    break;
2,400✔
1667
                                case col_type_String:
9,273✔
1668
                                    new_sub_ref = BPlusTree<StringData>::typed_write(sub_ref, out, m_alloc);
9,273✔
1669
                                    break;
9,273✔
1670
                                case col_type_Binary:
19,695✔
1671
                                    new_sub_ref = BPlusTree<BinaryData>::typed_write(sub_ref, out, m_alloc);
19,695✔
1672
                                    break;
19,695✔
1673
                                case col_type_Timestamp:
2,148✔
1674
                                    new_sub_ref = BPlusTree<Timestamp>::typed_write(sub_ref, out, m_alloc);
2,148✔
1675
                                    break;
2,148✔
1676
                                case col_type_Link:
2,471,238✔
1677
                                    new_sub_ref = BPlusTree<ObjKey>::typed_write(sub_ref, out, m_alloc);
2,471,238✔
1678
                                    break;
2,471,238✔
1679
                                case col_type_ObjectId:
13,512✔
1680
                                    new_sub_ref = BPlusTree<ObjectId>::typed_write(sub_ref, out, m_alloc);
13,512✔
1681
                                    break;
13,512✔
1682
                                case col_type_Decimal:
2,328✔
1683
                                    new_sub_ref = BPlusTree<Decimal128>::typed_write(sub_ref, out, m_alloc);
2,328✔
1684
                                    break;
2,328✔
1685
                                case col_type_UUID:
2,148✔
1686
                                    new_sub_ref = BPlusTree<UUID>::typed_write(sub_ref, out, m_alloc);
2,148✔
1687
                                    break;
2,148✔
1688
                                case col_type_Mixed:
6,912✔
1689
                                    new_sub_ref = BPlusTree<Mixed>::typed_write(sub_ref, out, m_alloc);
6,912✔
1690
                                    break;
6,912✔
NEW
1691
                                default:
✔
NEW
1692
                                    REALM_COMPILER_HINT_UNREACHABLE();
×
1693
                            }
2,561,784✔
1694
                        }
2,561,784✔
1695
                    }
2,605,596✔
1696
                    written_ref_leaf.set_as_ref(k, new_sub_ref);
4,352,337✔
1697
                }
4,352,337✔
1698
                new_ref = written_ref_leaf.write(out);
453,198✔
1699
            }
453,198✔
1700
            else if (col_type == col_type_BackLink) {
1,749,192✔
1701
                Array leaf(m_alloc);
172,701✔
1702
                leaf.init_from_ref(ref);
172,701✔
1703
                new_ref = leaf.write(out, true, only_modified, false);
172,701✔
1704
            }
172,701✔
1705
            else {
1,576,491✔
1706
                switch_on_type(col_key, [&](auto t) {
1,576,605✔
1707
                    using U = std::decay_t<decltype(*t)>;
1,576,605✔
1708
                    new_ref = ColumnTypeTraits<U>::cluster_leaf_type::typed_write(ref, out, m_alloc);
1,576,605✔
1709
                });
1,576,605✔
1710
            }
1,576,491✔
1711
        }
2,202,321✔
1712
        written_cluster.set_as_ref(j, new_ref);
2,250,723✔
1713
    }
2,250,723✔
1714
    return written_cluster.write(out);
1,067,175✔
1715
}
1,067,106✔
1716

1717
void Cluster::typed_print(std::string prefix) const
NEW
1718
{
×
NEW
1719
    REALM_ASSERT_DEBUG(!get_is_inner_bptree_node_from_header(get_header()));
×
NEW
1720
    std::cout << "Cluster of size " << size() << " " << header_to_string(get_header()) << std::endl;
×
NEW
1721
    const auto table = get_owning_table();
×
NEW
1722
    for (unsigned j = 0; j < size(); ++j) {
×
NEW
1723
        RefOrTagged rot = get_as_ref_or_tagged(j);
×
NEW
1724
        auto pref = prefix + "  " + std::to_string(j) + ":\t";
×
NEW
1725
        if (rot.is_ref() && rot.get_as_ref()) {
×
NEW
1726
            if (j == 0) {
×
NEW
1727
                std::cout << pref << "Keys as ArrayUnsigned as ";
×
NEW
1728
                Array a(m_alloc);
×
NEW
1729
                a.init_from_ref(rot.get_as_ref());
×
NEW
1730
                a.typed_print(pref);
×
NEW
1731
            }
×
NEW
1732
            else {
×
NEW
1733
                auto col_key = table->m_leaf_ndx2colkey[j - 1];
×
NEW
1734
                auto col_type = col_key.get_type();
×
NEW
1735
                auto col_attr = col_key.get_attrs();
×
NEW
1736
                std::string attr_string;
×
NEW
1737
                if (col_attr.test(col_attr_Dictionary))
×
NEW
1738
                    attr_string = "Dict:";
×
NEW
1739
                if (col_attr.test(col_attr_List))
×
NEW
1740
                    attr_string = "List:";
×
NEW
1741
                if (col_attr.test(col_attr_Set))
×
NEW
1742
                    attr_string = "Set:";
×
NEW
1743
                if (col_attr.test(col_attr_Nullable))
×
NEW
1744
                    attr_string += "Null:";
×
NEW
1745
                std::cout << pref << "Column[" << attr_string << col_type << "] as ";
×
1746
                // special cases for the types we want to compress
NEW
1747
                if (col_attr.test(col_attr_List) || col_attr.test(col_attr_Set)) {
×
1748
                    // That is a single bplustree
1749
                    // propagation of nullable missing here?
1750
                    // handling of mixed missing here?
NEW
1751
                    BPlusTreeBase::typed_print(pref, m_alloc, rot.get_as_ref(), col_type);
×
NEW
1752
                }
×
NEW
1753
                else if (col_attr.test(col_attr_Dictionary)) {
×
NEW
1754
                    Array dict_top(m_alloc);
×
NEW
1755
                    dict_top.init_from_ref(rot.get_as_ref());
×
NEW
1756
                    if (dict_top.size() == 0) {
×
NEW
1757
                        std::cout << "{ empty }" << std::endl;
×
NEW
1758
                        continue;
×
NEW
1759
                    }
×
NEW
1760
                    std::cout << "{" << std::endl;
×
NEW
1761
                    auto ref0 = dict_top.get_as_ref(0);
×
NEW
1762
                    if (ref0) {
×
NEW
1763
                        auto p = pref + "  0:\t";
×
NEW
1764
                        std::cout << p;
×
NEW
1765
                        BPlusTreeBase::typed_print(p, m_alloc, ref0, col_type);
×
NEW
1766
                    }
×
NEW
1767
                    if (dict_top.size() == 1) {
×
NEW
1768
                        continue; // is this really possible? or should all dicts have both trees?
×
NEW
1769
                    }
×
NEW
1770
                    auto ref1 = dict_top.get_as_ref(1);
×
NEW
1771
                    if (ref1) {
×
NEW
1772
                        auto p = pref + "  1:\t";
×
NEW
1773
                        std::cout << p;
×
NEW
1774
                        BPlusTreeBase::typed_print(p, m_alloc, dict_top.get_as_ref(1), col_type);
×
NEW
1775
                    }
×
NEW
1776
                }
×
NEW
1777
                else {
×
1778
                    // handle all other cases as generic arrays
NEW
1779
                    Array a(m_alloc);
×
NEW
1780
                    a.init_from_ref(rot.get_as_ref());
×
NEW
1781
                    a.typed_print(pref);
×
NEW
1782
                }
×
NEW
1783
            }
×
NEW
1784
        }
×
NEW
1785
    }
×
NEW
1786
}
×
1787

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