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

realm / realm-core / jorgen.edelbo_338

03 Jul 2024 03:00PM UTC coverage: 90.856% (-0.008%) from 90.864%
jorgen.edelbo_338

Pull #7803

Evergreen

nicola-cab
Merge branch 'next-major' into feature/string-compression
Pull Request #7803: Feature/string compression

103028 of 180606 branches covered (57.05%)

1144 of 1267 new or added lines in 33 files covered. (90.29%)

155 existing lines in 24 files now uncovered.

218583 of 240583 relevant lines covered (90.86%)

7959624.7 hits per line

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

74.78
/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)
507,096✔
46
{
1,036,506✔
47
    if (m_values.size() > 1) {
1,036,506✔
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,036,506✔
54

55
void FieldValues::insert(ColKey k, Mixed val, bool is_default)
56
{
866,526✔
57
    if (m_values.empty()) {
866,526✔
58
        m_values.emplace_back(k, val, is_default);
854,067✔
59
        return;
854,067✔
60
    }
854,067✔
61
    unsigned int idx = k.get_index().val;
12,459✔
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,459✔
66
}
12,459✔
67

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

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

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

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

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

97

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

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

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

109

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

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

125
    auto column_initialize = [this](ColKey col_key) {
202,305✔
126
        auto col_ndx = col_key.get_index();
202,305✔
127
        while (size() <= col_ndx.val + 1)
404,610✔
128
            add(0);
202,305✔
129
        auto type = col_key.get_type();
202,305✔
130
        auto attr = col_key.get_attrs();
202,305✔
131
        if (attr.test(col_attr_Collection)) {
202,305✔
132
            ArrayRef arr(m_alloc);
6,447✔
133
            arr.create();
6,447✔
134
            arr.set_parent(this, col_ndx.val + s_first_col_index);
6,447✔
135
            arr.update_parent();
6,447✔
136
            return IteratorControl::AdvanceToNext;
6,447✔
137
        }
6,447✔
138
        switch (type) {
195,858✔
139
            case col_type_Int:
117,339✔
140
                if (attr.test(col_attr_Nullable)) {
117,339✔
141
                    do_create<ArrayIntNull>(col_key);
13,863✔
142
                }
13,863✔
143
                else {
103,476✔
144
                    do_create<ArrayInteger>(col_key);
103,476✔
145
                }
103,476✔
146
                break;
117,339✔
147
            case col_type_Bool:
702✔
148
                do_create<ArrayBoolNull>(col_key);
702✔
149
                break;
702✔
150
            case col_type_Float:
1,128✔
151
                do_create<ArrayFloatNull>(col_key);
1,128✔
152
                break;
1,128✔
153
            case col_type_Double:
5,790✔
154
                do_create<ArrayDoubleNull>(col_key);
5,790✔
155
                break;
5,790✔
156
            case col_type_String: {
50,619✔
157
                if (m_tree_top.is_string_enum_type(col_ndx)) {
50,619✔
158
                    do_create<ArrayInteger>(col_key);
5,340✔
159
                }
5,340✔
160
                else {
45,279✔
161
                    do_create<ArrayString>(col_key);
45,279✔
162
                }
45,279✔
163
                break;
50,619✔
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,806✔
172
                do_create<ArrayTimestamp>(col_key);
4,806✔
173
                break;
4,806✔
174
            case col_type_Decimal:
276✔
175
                do_create<ArrayDecimal128>(col_key);
276✔
176
                break;
276✔
177
            case col_type_ObjectId:
2,877✔
178
                do_create<ArrayObjectIdNull>(col_key);
2,877✔
179
                break;
2,877✔
180
            case col_type_UUID:
408✔
181
                do_create<ArrayUUIDNull>(col_key);
408✔
182
                break;
408✔
183
            case col_type_Link:
2,277✔
184
                do_create<ArrayKey>(col_key);
2,277✔
185
                break;
2,277✔
186
            case col_type_TypedLink:
✔
187
                do_create<ArrayTypedLink>(col_key);
×
188
                break;
×
189
            case col_type_BackLink:
3,720✔
190
                do_create<ArrayBacklink>(col_key);
3,720✔
191
                break;
3,720✔
192
            default:
✔
193
                REALM_UNREACHABLE();
194
        }
195,858✔
195
        return IteratorControl::AdvanceToNext;
195,858✔
196
    };
195,858✔
197
    m_tree_top.m_owner->for_each_and_every_column(column_initialize);
104,802✔
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);
104,802✔
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();
104,802✔
205
}
104,802✔
206

207
void Cluster::init(MemRef mem)
208
{
336,671,919✔
209
    Array::init_from_mem(mem);
336,671,919✔
210
    auto rot = Array::get_as_ref_or_tagged(0);
336,671,919✔
211
    if (rot.is_tagged()) {
336,671,919✔
212
        m_keys.detach();
272,435,265✔
213
    }
272,435,265✔
214
    else {
64,236,654✔
215
        m_keys.init_from_ref(rot.get_as_ref());
64,236,654✔
216
    }
64,236,654✔
217
}
336,671,919✔
218

219
void Cluster::update_from_parent() noexcept
220
{
1,003,482✔
221
    Array::update_from_parent();
1,003,482✔
222
    auto rot = Array::get_as_ref_or_tagged(0);
1,003,482✔
223
    if (!rot.is_tagged()) {
1,003,482✔
224
        m_keys.update_from_parent();
32,439✔
225
    }
32,439✔
226
}
1,003,482✔
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
{
73,225,392✔
244
    auto rot = Array::get_as_ref_or_tagged(header, s_key_ref_or_size_index);
73,225,392✔
245
    if (rot.is_tagged()) {
73,225,392✔
246
        return size_t(rot.get_as_int());
71,802,237✔
247
    }
71,802,237✔
248
    else {
1,423,155✔
249
        return Array::get_size_from_header(alloc.translate(rot.get_as_ref()));
1,423,155✔
250
    }
1,423,155✔
251
}
73,225,392✔
252

253
template <class T>
254
inline void Cluster::set_string_interner(T&, ColKey) const
255
{
39,333,048✔
256
}
39,333,048✔
257

258
template <>
259
inline void Cluster::set_string_interner(ArrayString& arr, ColKey col_key) const
260
{
4,193,793✔
261
    m_tree_top.set_string_interner(arr, col_key);
4,193,793✔
262
}
4,193,793✔
263

264
template <>
265
inline void Cluster::set_string_interner(ArrayMixed& arr, ColKey col_key) const
266
{
315,552✔
267
    m_tree_top.set_string_interner(arr, col_key);
315,552✔
268
}
315,552✔
269

270
template <class T>
271
inline void Cluster::set_spec(T&, ColKey::Idx) const
272
{
39,288,849✔
273
}
39,288,849✔
274

275
template <>
276
inline void Cluster::set_spec(ArrayString& arr, ColKey::Idx col_ndx) const
277
{
4,190,322✔
278
    m_tree_top.set_spec(arr, col_ndx);
4,190,322✔
279
}
4,190,322✔
280

281
template <class T>
282
inline void Cluster::do_insert_row(size_t ndx, ColKey col, Mixed init_val, bool nullable)
283
{
36,004,356✔
284
    using U = typename util::RemoveOptional<typename T::value_type>::type;
36,004,356✔
285

286
    T arr(m_alloc);
36,004,356✔
287
    auto col_ndx = col.get_index();
36,004,356✔
288
    arr.set_parent(this, col_ndx.val + s_first_col_index);
36,004,356✔
289
    set_spec<T>(arr, col_ndx);
36,004,356✔
290
    set_string_interner<T>(arr, col);
36,004,356✔
291
    arr.init_from_parent();
36,004,356✔
292
    if (init_val.is_null()) {
36,004,356✔
293
        arr.insert(ndx, T::default_value(nullable));
35,006,592✔
294
    }
35,006,592✔
295
    else {
997,764✔
296
        arr.insert(ndx, init_val.get<U>());
997,764✔
297
    }
997,764✔
298
}
36,004,356✔
299

300
inline void Cluster::do_insert_key(size_t ndx, ColKey col_key, Mixed init_val, ObjKey origin_key)
301
{
278,340✔
302
    ObjKey target_key = init_val.is_null() ? ObjKey{} : init_val.get<ObjKey>();
278,340✔
303
    ArrayKey arr(m_alloc);
278,340✔
304
    auto col_ndx = col_key.get_index();
278,340✔
305
    arr.set_parent(this, col_ndx.val + s_first_col_index);
278,340✔
306
    arr.init_from_parent();
278,340✔
307
    arr.insert(ndx, target_key);
278,340✔
308

309
    // Insert backlink if link is not null
310
    if (target_key) {
278,340✔
311
        const Table* origin_table = m_tree_top.get_owning_table();
12✔
312
        ColKey opp_col = origin_table->get_opposite_column(col_key);
12✔
313
        TableRef opp_table = origin_table->get_opposite_table(col_key);
12✔
314
        Obj target_obj = opp_table->get_object(target_key);
12✔
315
        target_obj.add_backlink(opp_col, origin_key);
12✔
316
    }
12✔
317
}
278,340✔
318

319
inline void Cluster::do_insert_mixed(size_t ndx, ColKey col_key, Mixed init_value, ObjKey origin_key)
320
{
166,338✔
321
    ArrayMixed arr(m_alloc);
166,338✔
322
    arr.set_parent(this, col_key.get_index().val + s_first_col_index);
166,338✔
323
    set_string_interner(arr, col_key);
166,338✔
324
    arr.init_from_parent();
166,338✔
325
    arr.insert(ndx, init_value);
166,338✔
326

327
    // Insert backlink if needed
328
    if (init_value.is_type(type_TypedLink)) {
166,338✔
329
        // In case we are inserting in a Dictionary cluster, the backlink will
330
        // be handled in Dictionary::insert function
331
        if (Table* origin_table = const_cast<Table*>(m_tree_top.get_owning_table())) {
24✔
332
            if (origin_table->is_asymmetric()) {
24✔
333
                throw IllegalOperation("Object value not supported in asymmetric table");
6✔
334
            }
6✔
335
            ObjLink link = init_value.get<ObjLink>();
18✔
336
            auto target_table = origin_table->get_parent_group()->get_table(link.get_table_key());
18✔
337
            if (target_table->is_asymmetric()) {
18✔
338
                throw IllegalOperation("Ephemeral object value not supported");
6✔
339
            }
6✔
340
            ColKey backlink_col_key = target_table->find_or_add_backlink_column(col_key, origin_table->get_key());
12✔
341
            target_table->get_object(link.get_obj_key()).add_backlink(backlink_col_key, origin_key);
12✔
342
        }
12✔
343
    }
24✔
344
}
166,338✔
345

346
inline void Cluster::do_insert_link(size_t ndx, ColKey col_key, Mixed init_val, ObjKey origin_key)
347
{
×
348
    ObjLink target_link = init_val.is_null() ? ObjLink{} : init_val.get<ObjLink>();
×
349
    ArrayTypedLink arr(m_alloc);
×
350
    auto col_ndx = col_key.get_index();
×
351
    arr.set_parent(this, col_ndx.val + s_first_col_index);
×
352
    arr.init_from_parent();
×
353
    arr.insert(ndx, target_link);
×
354

355
    // Insert backlink if link is not null
356
    if (target_link) {
×
357
        Table* origin_table = const_cast<Table*>(m_tree_top.get_owning_table());
×
358
        auto target_table = origin_table->get_parent_group()->get_table(target_link.get_table_key());
×
359

360
        ColKey backlink_col_key = target_table->find_or_add_backlink_column(col_key, origin_table->get_key());
×
361
        target_table->get_object(target_link.get_obj_key()).add_backlink(backlink_col_key, origin_key);
×
362
    }
×
363
}
×
364

365
void Cluster::insert_row(size_t ndx, RowKey row_key, const FieldValues& init_values)
366
{
25,692,831✔
367
    // Ensure the cluster array is big enough to hold 64 bit values.
368
    copy_on_write(m_size * 8);
25,692,831✔
369

370
    if (m_keys.is_attached()) {
25,692,831✔
371
        m_keys.insert(ndx, row_key.value);
1,582,365✔
372
    }
1,582,365✔
373
    else {
24,110,466✔
374
        Array::set(s_key_ref_or_size_index, Array::get(s_key_ref_or_size_index) + 2); // Increments size by 1
24,110,466✔
375
    }
24,110,466✔
376

377
    auto val = init_values.begin();
25,692,831✔
378
    auto insert_in_column = [&](ColKey col_key) {
38,309,232✔
379
        auto col_ndx = col_key.get_index();
38,309,232✔
380
        auto attr = col_key.get_attrs();
38,309,232✔
381
        Mixed init_value;
38,309,232✔
382
        // init_values must be sorted in col_ndx order - this is ensured by ClustTree::insert()
383
        if (val != init_values.end() && val->col_key.get_index().val == col_ndx.val) {
38,309,232✔
384
            init_value = val->value;
933,069✔
385
            ++val;
933,069✔
386
        }
933,069✔
387

388
        auto type = col_key.get_type();
38,309,232✔
389
        if (attr.test(col_attr_Collection)) {
38,309,232✔
390
            REALM_ASSERT(init_value.is_null());
751,194✔
391
            ArrayRef arr(m_alloc);
751,194✔
392
            arr.set_parent(this, col_ndx.val + s_first_col_index);
751,194✔
393
            arr.init_from_parent();
751,194✔
394
            arr.insert(ndx, 0);
751,194✔
395
            return IteratorControl::AdvanceToNext;
751,194✔
396
        }
751,194✔
397

398
        bool nullable = attr.test(col_attr_Nullable);
37,558,038✔
399
        ObjKey obj_key(int64_t(row_key.value + get_offset()));
37,558,038✔
400
        switch (type) {
37,558,038✔
401
            case col_type_Int:
27,743,046✔
402
                if (attr.test(col_attr_Nullable)) {
27,743,046✔
403
                    do_insert_row<ArrayIntNull>(ndx, col_key, init_value, nullable);
2,905,449✔
404
                }
2,905,449✔
405
                else {
24,837,597✔
406
                    do_insert_row<ArrayInteger>(ndx, col_key, init_value, nullable);
24,837,597✔
407
                }
24,837,597✔
408
                break;
27,743,046✔
409
            case col_type_Bool:
188,286✔
410
                do_insert_row<ArrayBoolNull>(ndx, col_key, init_value, nullable);
188,286✔
411
                break;
188,286✔
412
            case col_type_Float:
326,541✔
413
                do_insert_row<ArrayFloatNull>(ndx, col_key, init_value, nullable);
326,541✔
414
                break;
326,541✔
415
            case col_type_Double:
1,493,337✔
416
                do_insert_row<ArrayDoubleNull>(ndx, col_key, init_value, nullable);
1,493,337✔
417
                break;
1,493,337✔
418
            case col_type_String:
3,691,467✔
419
                do_insert_row<ArrayString>(ndx, col_key, init_value, nullable);
3,691,467✔
420
                break;
3,691,467✔
421
            case col_type_Binary:
1,360,254✔
422
                do_insert_row<ArrayBinary>(ndx, col_key, init_value, nullable);
1,360,254✔
423
                break;
1,360,254✔
424
            case col_type_Mixed: {
166,338✔
425
                do_insert_mixed(ndx, col_key, init_value, obj_key);
166,338✔
426
                break;
166,338✔
427
            }
×
428
            case col_type_Timestamp:
824,916✔
429
                do_insert_row<ArrayTimestamp>(ndx, col_key, init_value, nullable);
824,916✔
430
                break;
824,916✔
431
            case col_type_Decimal:
78,222✔
432
                do_insert_row<ArrayDecimal128>(ndx, col_key, init_value, nullable);
78,222✔
433
                break;
78,222✔
434
            case col_type_ObjectId:
173,478✔
435
                do_insert_row<ArrayObjectIdNull>(ndx, col_key, init_value, nullable);
173,478✔
436
                break;
173,478✔
437
            case col_type_UUID:
119,652✔
438
                do_insert_row<ArrayUUIDNull>(ndx, col_key, init_value, nullable);
119,652✔
439
                break;
119,652✔
440
            case col_type_Link:
278,340✔
441
                do_insert_key(ndx, col_key, init_value, obj_key);
278,340✔
442
                break;
278,340✔
443
            case col_type_TypedLink:
✔
444
                do_insert_link(ndx, col_key, init_value, obj_key);
×
445
                break;
×
446
            case col_type_BackLink: {
1,150,614✔
447
                ArrayBacklink arr(m_alloc);
1,150,614✔
448
                arr.set_parent(this, col_ndx.val + s_first_col_index);
1,150,614✔
449
                arr.init_from_parent();
1,150,614✔
450
                arr.insert(ndx, 0);
1,150,614✔
451
                break;
1,150,614✔
452
            }
×
453
            default:
✔
454
                REALM_ASSERT(false);
×
455
                break;
×
456
        }
37,558,038✔
457
        return IteratorControl::AdvanceToNext;
37,492,362✔
458
    };
37,558,038✔
459
    m_tree_top.m_owner->for_each_and_every_column(insert_in_column);
25,692,831✔
460
}
25,692,831✔
461

462
template <class T>
463
inline void Cluster::do_move(size_t ndx, ColKey col_key, Cluster* to)
464
{
24,264✔
465
    auto col_ndx = col_key.get_index().val + s_first_col_index;
24,264✔
466
    T src(m_alloc);
24,264✔
467
    src.set_parent(this, col_ndx);
24,264✔
468
    src.init_from_parent();
24,264✔
469
    set_string_interner<T>(src, col_key);
24,264✔
470

471
    T dst(m_alloc);
24,264✔
472
    dst.set_parent(to, col_ndx);
24,264✔
473
    dst.init_from_parent();
24,264✔
474
    set_string_interner<T>(dst, col_key);
24,264✔
475

476
    src.move(dst, ndx);
24,264✔
477
}
24,264✔
478

479
void Cluster::move(size_t ndx, ClusterNode* new_node, int64_t offset)
480
{
12,711✔
481
    auto new_leaf = static_cast<Cluster*>(new_node);
12,711✔
482

483
    auto move_from_column = [&](ColKey col_key) {
24,264✔
484
        auto attr = col_key.get_attrs();
24,264✔
485
        auto type = col_key.get_type();
24,264✔
486

487
        if (attr.test(col_attr_Collection)) {
24,264✔
488
            do_move<ArrayRef>(ndx, col_key, new_leaf);
294✔
489
            return IteratorControl::AdvanceToNext;
294✔
490
        }
294✔
491

492
        switch (type) {
23,970✔
493
            case col_type_Int:
21,348✔
494
                if (attr.test(col_attr_Nullable)) {
21,348✔
495
                    do_move<ArrayIntNull>(ndx, col_key, new_leaf);
9,663✔
496
                }
9,663✔
497
                else {
11,685✔
498
                    do_move<ArrayInteger>(ndx, col_key, new_leaf);
11,685✔
499
                }
11,685✔
500
                break;
21,348✔
501
            case col_type_Bool:
36✔
502
                do_move<ArrayBoolNull>(ndx, col_key, new_leaf);
36✔
503
                break;
36✔
504
            case col_type_Float:
36✔
505
                do_move<ArrayFloat>(ndx, col_key, new_leaf);
36✔
506
                break;
36✔
507
            case col_type_Double:
36✔
508
                do_move<ArrayDouble>(ndx, col_key, new_leaf);
36✔
509
                break;
36✔
510
            case col_type_String: {
1,044✔
511
                if (m_tree_top.is_string_enum_type(col_key.get_index()))
1,044✔
512
                    do_move<ArrayInteger>(ndx, col_key, new_leaf);
×
513
                else
1,044✔
514
                    do_move<ArrayString>(ndx, col_key, new_leaf);
1,044✔
515
                break;
1,044✔
516
            }
×
517
            case col_type_Binary:
36✔
518
                do_move<ArrayBinary>(ndx, col_key, new_leaf);
36✔
519
                break;
36✔
520
            case col_type_Mixed:
528✔
521
                do_move<ArrayMixed>(ndx, col_key, new_leaf);
528✔
522
                break;
528✔
523
            case col_type_Timestamp:
36✔
524
                do_move<ArrayTimestamp>(ndx, col_key, new_leaf);
36✔
525
                break;
36✔
526
            case col_type_Decimal:
36✔
527
                do_move<ArrayDecimal128>(ndx, col_key, new_leaf);
36✔
528
                break;
36✔
529
            case col_type_ObjectId:
36✔
530
                do_move<ArrayObjectIdNull>(ndx, col_key, new_leaf);
36✔
531
                break;
36✔
532
            case col_type_UUID:
120✔
533
                do_move<ArrayUUIDNull>(ndx, col_key, new_leaf);
120✔
534
                break;
120✔
535
            case col_type_Link:
96✔
536
                do_move<ArrayKey>(ndx, col_key, new_leaf);
96✔
537
                break;
96✔
538
            case col_type_TypedLink:
✔
539
                do_move<ArrayTypedLink>(ndx, col_key, new_leaf);
×
540
                break;
×
541
            case col_type_BackLink:
582✔
542
                do_move<ArrayBacklink>(ndx, col_key, new_leaf);
582✔
543
                break;
582✔
544
            default:
✔
545
                REALM_ASSERT(false);
×
546
                break;
×
547
        }
23,970✔
548
        return IteratorControl::AdvanceToNext;
23,970✔
549
    };
23,970✔
550
    m_tree_top.m_owner->for_each_and_every_column(move_from_column);
12,711✔
551
    for (size_t i = ndx; i < m_keys.size(); i++) {
1,358,898✔
552
        new_leaf->m_keys.add(m_keys.get(i) - offset);
1,346,187✔
553
    }
1,346,187✔
554
    m_keys.truncate(ndx);
12,711✔
555
}
12,711✔
556

557
Cluster::~Cluster() {}
342,462,618✔
558

559
ColKey Cluster::get_col_key(size_t ndx_in_parent) const
560
{
18,432✔
561
    ColKey::Idx col_ndx{unsigned(ndx_in_parent - 1)}; // <- leaf_index here. Opaque.
18,432✔
562
    auto col_key = get_owning_table()->leaf_ndx2colkey(col_ndx);
18,432✔
563
    REALM_ASSERT(col_key.get_index().val == col_ndx.val);
18,432✔
564
    return col_key;
18,432✔
565
}
18,432✔
566

567
void Cluster::ensure_general_form()
568
{
65,163✔
569
    if (!m_keys.is_attached()) {
65,163✔
570
        size_t current_size = get_size_in_compact_form();
49,638✔
571
        m_keys.create(current_size, 255);
49,638✔
572
        m_keys.update_parent();
49,638✔
573
        for (size_t i = 0; i < current_size; i++) {
5,800,518✔
574
            m_keys.set(i, i);
5,750,880✔
575
        }
5,750,880✔
576
    }
49,638✔
577
}
65,163✔
578

579
template <class T>
580
inline void Cluster::do_insert_column(ColKey col_key, bool nullable)
581
{
611,703✔
582
    size_t sz = node_size();
611,703✔
583

584
    T arr(m_alloc);
611,703✔
585
    arr.create();
611,703✔
586
    auto val = T::default_value(nullable);
611,703✔
587
    for (size_t i = 0; i < sz; i++) {
6,848,859✔
588
        arr.add(val);
6,237,156✔
589
    }
6,237,156✔
590
    auto col_ndx = col_key.get_index();
611,703✔
591
    unsigned ndx = col_ndx.val + s_first_col_index;
611,703✔
592

593
    // Fill up if indexes are not consecutive
594
    while (size() < ndx)
611,703✔
595
        Array::add(0);
×
596

597
    if (ndx == size())
611,703✔
598
        Array::insert(ndx, from_ref(arr.get_ref()));
611,610✔
599
    else
93✔
600
        Array::set(ndx, from_ref(arr.get_ref()));
93✔
601
}
611,703✔
602

603
void Cluster::insert_column(ColKey col_key)
604
{
806,754✔
605
    auto attr = col_key.get_attrs();
806,754✔
606
    auto type = col_key.get_type();
806,754✔
607
    if (attr.test(col_attr_Collection)) {
806,754✔
608
        size_t sz = node_size();
195,045✔
609

610
        ArrayRef arr(m_alloc);
195,045✔
611
        arr.create(sz);
195,045✔
612
        auto col_ndx = col_key.get_index();
195,045✔
613
        unsigned idx = col_ndx.val + s_first_col_index;
195,045✔
614
        if (idx == size())
195,045✔
615
            Array::insert(idx, from_ref(arr.get_ref()));
195,045✔
616
        else
×
617
            Array::set(idx, from_ref(arr.get_ref()));
×
618
        return;
195,045✔
619
    }
195,045✔
620
    bool nullable = attr.test(col_attr_Nullable);
611,709✔
621
    switch (type) {
611,709✔
622
        case col_type_Int:
275,166✔
623
            if (nullable) {
275,166✔
624
                do_insert_column<ArrayIntNull>(col_key, nullable);
32,814✔
625
            }
32,814✔
626
            else {
242,352✔
627
                do_insert_column<ArrayInteger>(col_key, nullable);
242,352✔
628
            }
242,352✔
629
            break;
275,166✔
630
        case col_type_Bool:
5,487✔
631
            do_insert_column<ArrayBoolNull>(col_key, nullable);
5,487✔
632
            break;
5,487✔
633
        case col_type_Float:
6,450✔
634
            do_insert_column<ArrayFloatNull>(col_key, nullable);
6,450✔
635
            break;
6,450✔
636
        case col_type_Double:
7,077✔
637
            do_insert_column<ArrayDoubleNull>(col_key, nullable);
7,077✔
638
            break;
7,077✔
639
        case col_type_String:
101,568✔
640
            do_insert_column<ArrayString>(col_key, nullable);
101,568✔
641
            break;
101,568✔
642
        case col_type_Binary:
6,891✔
643
            do_insert_column<ArrayBinary>(col_key, nullable);
6,891✔
644
            break;
6,891✔
645
        case col_type_Mixed:
9,114✔
646
            do_insert_column<ArrayMixed>(col_key, nullable);
9,114✔
647
            break;
9,114✔
648
        case col_type_Timestamp:
23,631✔
649
            do_insert_column<ArrayTimestamp>(col_key, nullable);
23,631✔
650
            break;
23,631✔
651
        case col_type_Decimal:
5,022✔
652
            do_insert_column<ArrayDecimal128>(col_key, nullable);
5,022✔
653
            break;
5,022✔
654
        case col_type_ObjectId:
50,577✔
655
            do_insert_column<ArrayObjectIdNull>(col_key, nullable);
50,577✔
656
            break;
50,577✔
657
        case col_type_UUID:
5,616✔
658
            do_insert_column<ArrayUUIDNull>(col_key, nullable);
5,616✔
659
            break;
5,616✔
660
        case col_type_Link:
31,146✔
661
            do_insert_column<ArrayKey>(col_key, nullable);
31,146✔
662
            break;
31,146✔
663
        case col_type_TypedLink:
✔
664
            do_insert_column<ArrayTypedLink>(col_key, nullable);
×
665
            break;
×
666
        case col_type_BackLink:
83,961✔
667
            do_insert_column<ArrayBacklink>(col_key, nullable);
83,961✔
668
            break;
83,961✔
669
        default:
✔
670
            REALM_UNREACHABLE();
671
            break;
×
672
    }
611,709✔
673
}
611,709✔
674

675
void Cluster::remove_column(ColKey col_key)
676
{
6,537✔
677
    auto col_ndx = col_key.get_index();
6,537✔
678
    unsigned idx = col_ndx.val + s_first_col_index;
6,537✔
679
    ref_type ref = to_ref(Array::get(idx));
6,537✔
680
    if (ref != 0) {
6,537✔
681
        Array::destroy_deep(ref, m_alloc);
6,537✔
682
    }
6,537✔
683
    if (idx == size() - 1)
6,537✔
684
        Array::erase(idx);
4,995✔
685
    else
1,542✔
686
        Array::set(idx, 0);
1,542✔
687
}
6,537✔
688

689
ref_type Cluster::insert(RowKey row_key, const FieldValues& init_values, ClusterNode::State& state)
690
{
25,669,689✔
691
    int64_t current_key_value = -1;
25,669,689✔
692
    size_t sz;
25,669,689✔
693
    size_t ndx;
25,669,689✔
694
    ref_type ret = 0;
25,669,689✔
695

696
    auto on_error = [&] {
25,669,689✔
697
        throw KeyAlreadyUsed(
15✔
698
            util::format("When inserting key '%1' in '%2'", row_key.value, get_owning_table()->get_name()));
15✔
699
    };
15✔
700

701
    if (m_keys.is_attached()) {
25,669,689✔
702
        sz = m_keys.size();
1,570,956✔
703
        ndx = m_keys.lower_bound(row_key.value);
1,570,956✔
704
        if (ndx < sz) {
1,570,956✔
705
            current_key_value = m_keys.get(ndx);
616,935✔
706
            if (row_key.value == uint64_t(current_key_value)) {
616,935✔
707
                on_error();
15✔
708
            }
15✔
709
        }
616,935✔
710
    }
1,570,956✔
711
    else {
24,098,733✔
712
        sz = size_t(Array::get(s_key_ref_or_size_index)) >> 1; // Size is stored as tagged integer
24,098,733✔
713
        if (row_key.value < sz) {
24,098,733✔
714
            on_error();
×
715
        }
×
716
        // Key value is bigger than all other values, should be put last
717
        ndx = sz;
24,098,733✔
718
        if (row_key.value > sz && sz < cluster_node_size) {
24,098,733✔
719
            ensure_general_form();
17,034✔
720
        }
17,034✔
721
    }
24,098,733✔
722

723
    REALM_ASSERT_DEBUG(sz <= cluster_node_size);
25,669,689✔
724
    if (REALM_LIKELY(sz < cluster_node_size)) {
25,669,689✔
725
        insert_row(ndx, row_key, init_values); // Throws
25,590,666✔
726
        state.mem = get_mem();
25,590,666✔
727
        state.index = ndx;
25,590,666✔
728
    }
25,590,666✔
729
    else {
79,023✔
730
        // Split leaf node
731
        Cluster new_leaf(0, m_alloc, m_tree_top);
79,023✔
732
        new_leaf.create();
79,023✔
733
        if (ndx == sz) {
93,885✔
734
            new_leaf.insert_row(0, RowKey(0), init_values); // Throws
93,885✔
735
            state.split_key = int64_t(row_key.value);
93,885✔
736
            state.mem = new_leaf.get_mem();
93,885✔
737
            state.index = 0;
93,885✔
738
        }
93,885✔
739
        else {
4,294,967,294✔
740
            // Current cluster must be in general form to get here
741
            REALM_ASSERT_DEBUG(m_keys.is_attached());
4,294,967,294✔
742
            new_leaf.ensure_general_form();
4,294,967,294✔
743
            move(ndx, &new_leaf, current_key_value);
4,294,967,294✔
744
            insert_row(ndx, row_key, init_values); // Throws
4,294,967,294✔
745
            state.mem = get_mem();
4,294,967,294✔
746
            state.split_key = current_key_value;
4,294,967,294✔
747
            state.index = ndx;
4,294,967,294✔
748
        }
4,294,967,294✔
749
        ret = new_leaf.get_ref();
79,023✔
750
    }
79,023✔
751

752
    return ret;
25,669,689✔
753
}
25,669,689✔
754

755
bool Cluster::try_get(RowKey k, ClusterNode::State& state) const noexcept
756
{
285,566,562✔
757
    state.mem = get_mem();
285,566,562✔
758
    if (m_keys.is_attached()) {
285,566,562✔
759
        state.index = m_keys.lower_bound(k.value);
51,021,981✔
760
        return state.index != m_keys.size() && m_keys.get(state.index) == k.value;
51,021,981✔
761
    }
51,021,981✔
762
    else {
234,544,581✔
763
        if (k.value < uint64_t(Array::get(s_key_ref_or_size_index) >> 1)) {
234,544,581✔
764
            state.index = size_t(k.value);
216,132,930✔
765
            return true;
216,132,930✔
766
        }
216,132,930✔
767
    }
234,544,581✔
768
    return false;
18,411,651✔
769
}
285,566,562✔
770

771
ObjKey Cluster::get(size_t ndx, ClusterNode::State& state) const
772
{
15,717,591✔
773
    state.index = ndx;
15,717,591✔
774
    state.mem = get_mem();
15,717,591✔
775
    return get_real_key(ndx);
15,717,591✔
776
}
15,717,591✔
777

778
template <class T>
779
inline void Cluster::do_erase(size_t ndx, ColKey col_key)
780
{
6,927,084✔
781
    auto col_ndx = col_key.get_index();
6,927,084✔
782
    T values(m_alloc);
6,927,084✔
783
    values.set_parent(this, col_ndx.val + s_first_col_index);
6,927,084✔
784
    set_spec<T>(values, col_ndx);
6,927,084✔
785
    set_string_interner<T>(values, col_key);
6,927,084✔
786
    values.init_from_parent();
6,927,084✔
787
    if constexpr (std::is_same_v<T, ArrayTypedLink>) {
6,927,084✔
788
        if (ObjLink link = values.get(ndx)) {
×
789
            if (const Table* origin_table = m_tree_top.get_owning_table()) {
×
790
                auto target_obj = origin_table->get_parent_group()->get_object(link);
×
791

792
                ColKey backlink_col_key =
×
793
                    target_obj.get_table()->find_backlink_column(col_key, origin_table->get_key());
×
794
                REALM_ASSERT(backlink_col_key);
×
795
                target_obj.remove_one_backlink(backlink_col_key, get_real_key(ndx)); // Throws
×
796
            }
×
797
        }
×
798
    }
×
799
    values.erase(ndx);
6,927,084✔
800
}
6,927,084✔
801

802
inline void Cluster::do_erase_mixed(size_t ndx, ColKey col_key, CascadeState& state)
803
{
145,437✔
804
    const Table* origin_table = m_tree_top.get_owning_table();
145,437✔
805
    auto col_ndx = col_key.get_index();
145,437✔
806

807
    ArrayMixed values(m_alloc);
145,437✔
808
    values.set_parent(this, col_ndx.val + s_first_col_index);
145,437✔
809
    set_string_interner(values, col_key);
145,437✔
810
    values.init_from_parent();
145,437✔
811

812
    Mixed value = values.get(ndx);
145,437✔
813
    if (value.is_type(type_TypedLink)) {
145,437✔
814
        ObjLink link = value.get<ObjLink>();
30✔
815
        Obj obj(origin_table->m_own_ref, get_mem(), get_real_key(ndx), ndx);
30✔
816
        obj.remove_backlink(col_key, link, state);
30✔
817
    }
30✔
818
    if (value.is_type(type_List)) {
145,437✔
819
        Obj obj(origin_table->m_own_ref, get_mem(), get_real_key(ndx), ndx);
72,042✔
820
        Lst<Mixed> list(obj, col_key);
72,042✔
821
        list.remove_backlinks(state);
72,042✔
822
    }
72,042✔
823
    if (value.is_type(type_Dictionary)) {
145,437✔
824
        Obj obj(origin_table->m_own_ref, get_mem(), get_real_key(ndx), ndx);
72,018✔
825
        Dictionary dict(obj, col_key);
72,018✔
826
        dict.remove_backlinks(state);
72,018✔
827
    }
72,018✔
828
    values.erase(ndx);
145,437✔
829
}
145,437✔
830

831
inline void Cluster::do_erase_key(size_t ndx, ColKey col_key, CascadeState& state)
832
{
92,133✔
833
    ArrayKey values(m_alloc);
92,133✔
834
    auto col_ndx = col_key.get_index();
92,133✔
835
    values.set_parent(this, col_ndx.val + s_first_col_index);
92,133✔
836
    values.init_from_parent();
92,133✔
837

838
    ObjKey key = values.get(ndx);
92,133✔
839
    if (key != null_key) {
92,133✔
840
        do_remove_backlinks(get_real_key(ndx), col_key, std::vector<ObjKey>{key}, state);
89,370✔
841
    }
89,370✔
842
    values.erase(ndx);
92,133✔
843
}
92,133✔
844

845
size_t Cluster::get_ndx(RowKey k, size_t ndx) const noexcept
846
{
10,502,127✔
847
    size_t index;
10,502,127✔
848
    if (m_keys.is_attached()) {
10,502,127✔
849
        index = m_keys.lower_bound(k.value);
10,384,887✔
850
        if (index == m_keys.size() || m_keys.get(index) != k.value) {
10,385,805✔
851
            return realm::npos;
18✔
852
        }
18✔
853
    }
10,384,887✔
854
    else {
117,240✔
855
        index = size_t(k.value);
117,240✔
856
        if (index >= get_as_ref_or_tagged(s_key_ref_or_size_index).get_as_int()) {
117,240✔
857
            return realm::npos;
×
858
        }
×
859
    }
117,240✔
860
    return index + ndx;
10,502,109✔
861
}
10,502,127✔
862

863
size_t Cluster::erase(RowKey row_key, CascadeState& state)
864
{
5,282,163✔
865
    size_t ndx = get_ndx(row_key, 0);
5,282,163✔
866
    if (ndx == realm::npos)
5,282,163✔
867
        throw KeyNotFound(util::format("When erasing key '%1' (offset '%2') in '%3'", row_key.value, m_offset,
18✔
868
                                       get_owning_table()->get_name()));
18✔
869

870
    ObjKey real_key = get_real_key(ndx);
5,282,145✔
871
    std::vector<ColKey> backlink_column_keys;
5,282,145✔
872

873
    auto erase_in_column = [&](ColKey col_key) {
7,267,866✔
874
        auto col_type = col_key.get_type();
7,267,866✔
875
        auto attr = col_key.get_attrs();
7,267,866✔
876
        if (attr.test(col_attr_Collection)) {
7,267,866✔
877
            auto col_ndx = col_key.get_index();
103,641✔
878
            ArrayRef values(m_alloc);
103,641✔
879
            values.set_parent(this, col_ndx.val + s_first_col_index);
103,641✔
880
            values.init_from_parent();
103,641✔
881
            ref_type ref = values.get(ndx);
103,641✔
882

883
            if (ref) {
103,641✔
884
                const Table* origin_table = m_tree_top.get_owning_table();
83,361✔
885
                if (attr.test(col_attr_Dictionary)) {
83,361✔
886
                    if (col_type == col_type_Mixed || col_type == col_type_Link) {
38,655✔
887
                        Obj obj(origin_table->m_own_ref, get_mem(), real_key, ndx);
36,354✔
888
                        Dictionary dict(obj, col_key);
36,354✔
889
                        dict.remove_backlinks(state);
36,354✔
890
                    }
36,354✔
891
                }
38,655✔
892
                else if (col_type == col_type_Link) {
44,706✔
893
                    BPlusTree<ObjKey> links(m_alloc);
3,930✔
894
                    links.init_from_ref(ref);
3,930✔
895
                    if (links.size() > 0) {
3,930✔
896
                        do_remove_backlinks(real_key, col_key, links.get_all(), state);
996✔
897
                    }
996✔
898
                }
3,930✔
899
                else if (col_type == col_type_TypedLink) {
40,776✔
900
                    BPlusTree<ObjLink> links(m_alloc);
×
901
                    links.init_from_ref(ref);
×
902
                    for (size_t i = 0; i < links.size(); i++) {
×
903
                        ObjLink link = links.get(i);
×
904
                        auto target_obj = origin_table->get_parent_group()->get_object(link);
×
905
                        ColKey backlink_col_key =
×
906
                            target_obj.get_table()->find_backlink_column(col_key, origin_table->get_key());
×
907
                        target_obj.remove_one_backlink(backlink_col_key, real_key);
×
908
                    }
×
909
                }
×
910
                else if (col_type == col_type_Mixed) {
40,776✔
911
                    Obj obj(origin_table->m_own_ref, get_mem(), real_key, ndx);
36,246✔
912
                    Lst<Mixed> list(obj, col_key);
36,246✔
913
                    list.remove_backlinks(state);
36,246✔
914
                }
36,246✔
915
                Array::destroy_deep(ref, m_alloc);
83,361✔
916
            }
83,361✔
917

918
            values.erase(ndx);
103,641✔
919

920
            return IteratorControl::AdvanceToNext;
103,641✔
921
        }
103,641✔
922

923
        switch (col_type) {
7,164,225✔
924
            case col_type_Int:
5,984,640✔
925
                if (attr.test(col_attr_Nullable)) {
5,984,640✔
926
                    do_erase<ArrayIntNull>(ndx, col_key);
1,275,792✔
927
                }
1,275,792✔
928
                else {
4,708,848✔
929
                    do_erase<ArrayInteger>(ndx, col_key);
4,708,848✔
930
                }
4,708,848✔
931
                break;
5,984,640✔
932
            case col_type_Bool:
42,411✔
933
                do_erase<ArrayBoolNull>(ndx, col_key);
42,411✔
934
                break;
42,411✔
935
            case col_type_Float:
36,486✔
936
                do_erase<ArrayFloatNull>(ndx, col_key);
36,486✔
937
                break;
36,486✔
938
            case col_type_Double:
36,498✔
939
                do_erase<ArrayDoubleNull>(ndx, col_key);
36,498✔
940
                break;
36,498✔
941
            case col_type_String:
357,096✔
942
                do_erase<ArrayString>(ndx, col_key);
357,096✔
943
                break;
357,096✔
944
            case col_type_Binary:
39,597✔
945
                do_erase<ArrayBinary>(ndx, col_key);
39,597✔
946
                break;
39,597✔
947
            case col_type_Mixed:
145,437✔
948
                do_erase_mixed(ndx, col_key, state);
145,437✔
949
                break;
145,437✔
950
            case col_type_Timestamp:
45,216✔
951
                do_erase<ArrayTimestamp>(ndx, col_key);
45,216✔
952
                break;
45,216✔
953
            case col_type_Decimal:
36,279✔
954
                do_erase<ArrayDecimal128>(ndx, col_key);
36,279✔
955
                break;
36,279✔
956
            case col_type_ObjectId:
38,736✔
957
                do_erase<ArrayObjectIdNull>(ndx, col_key);
38,736✔
958
                break;
38,736✔
959
            case col_type_UUID:
60,285✔
960
                do_erase<ArrayUUIDNull>(ndx, col_key);
60,285✔
961
                break;
60,285✔
962
            case col_type_Link:
92,133✔
963
                do_erase_key(ndx, col_key, state);
92,133✔
964
                break;
92,133✔
965
            case col_type_TypedLink:
✔
966
                do_erase<ArrayTypedLink>(ndx, col_key);
×
967
                break;
×
968
            case col_type_BackLink:
249,834✔
969
                if (state.m_mode == CascadeState::Mode::None) {
249,834✔
970
                    do_erase<ArrayBacklink>(ndx, col_key);
188,625✔
971
                }
188,625✔
972
                else {
61,209✔
973
                    // Postpone the deletion of backlink entries or else the
974
                    // checks for if there's any remaining backlinks will
975
                    // check the wrong row for columns which have already
976
                    // had values erased from them.
977
                    backlink_column_keys.push_back(col_key);
61,209✔
978
                }
61,209✔
979
                break;
249,834✔
980
            default:
✔
981
                REALM_ASSERT(false);
×
982
                break;
×
983
        }
7,164,225✔
984
        return IteratorControl::AdvanceToNext;
7,164,018✔
985
    };
7,164,225✔
986
    m_tree_top.m_owner->for_each_and_every_column(erase_in_column);
5,282,145✔
987

988
    // Any remaining backlink columns to erase from?
989
    for (auto k : backlink_column_keys)
5,282,145✔
990
        do_erase<ArrayBacklink>(ndx, k);
61,215✔
991

992
    if (m_keys.is_attached()) {
5,282,145✔
993
        m_keys.erase(ndx);
5,240,490✔
994
    }
5,240,490✔
995
    else {
41,655✔
996
        size_t current_size = get_size_in_compact_form();
41,655✔
997
        if (ndx == current_size - 1) {
41,655✔
998
            // When deleting last, we can still maintain compact form
999
            set(0, RefOrTagged::make_tagged(current_size - 1));
26,286✔
1000
        }
26,286✔
1001
        else {
15,369✔
1002
            ensure_general_form();
15,369✔
1003
            m_keys.erase(ndx);
15,369✔
1004
        }
15,369✔
1005
    }
41,655✔
1006

1007
    return node_size();
5,282,145✔
1008
}
5,282,163✔
1009

1010
void Cluster::nullify_incoming_links(RowKey key, CascadeState& state)
1011
{
5,183,931✔
1012
    size_t ndx = get_ndx(key, 0);
5,183,931✔
1013
    if (ndx == realm::npos)
5,183,931✔
1014
        throw KeyNotFound(util::format("Key '%1' not found in '%2' when nullifying incoming links", key.value,
×
1015
                                       get_owning_table()->get_class_name()));
×
1016

1017
    // We must start with backlink columns in case the corresponding link
1018
    // columns are in the same table so that we can nullify links before
1019
    // erasing rows in the link columns.
1020
    //
1021
    // This phase also generates replication instructions documenting the side-
1022
    // effects of deleting the object (i.e. link nullifications). These instructions
1023
    // must come before the actual deletion of the object, but at the same time
1024
    // the Replication object may need a consistent view of the row (not including
1025
    // link columns). Therefore we first nullify links to this object, then
1026
    // generate the instruction, and then delete the row in the remaining columns.
1027

1028
    auto nullify_fwd_links = [&](ColKey col_key) {
5,183,931✔
1029
        ColKey::Idx leaf_ndx = col_key.get_index();
232,371✔
1030
        auto type = col_key.get_type();
232,371✔
1031
        REALM_ASSERT(type == col_type_BackLink);
232,371✔
1032
        ArrayBacklink values(m_alloc);
232,371✔
1033
        values.set_parent(this, leaf_ndx.val + s_first_col_index);
232,371✔
1034
        values.init_from_parent();
232,371✔
1035
        // Ensure that Cluster is writable and able to hold references to nodes in
1036
        // the slab area before nullifying or deleting links. These operation may
1037
        // both have the effect that other objects may be constructed and manipulated.
1038
        // If those other object are in the same cluster that the object to be deleted
1039
        // is in, then that will cause another accessor to this cluster to be created.
1040
        // It would lead to an error if the cluster node was relocated without it being
1041
        // reflected in the context here.
1042
        values.copy_on_write();
232,371✔
1043
        values.nullify_fwd_links(ndx, state);
232,371✔
1044

1045
        return IteratorControl::AdvanceToNext;
232,371✔
1046
    };
232,371✔
1047

1048
    m_tree_top.get_owning_table()->for_each_backlink_column(nullify_fwd_links);
5,183,931✔
1049
}
5,183,931✔
1050

1051
void Cluster::upgrade_string_to_enum(ColKey col_key, ArrayString& keys)
1052
{
1,086✔
1053
    auto col_ndx = col_key.get_index();
1,086✔
1054
    Array indexes(m_alloc);
1,086✔
1055
    indexes.create(Array::type_Normal, false);
1,086✔
1056
    ArrayString values(m_alloc);
1,086✔
1057
    ref_type ref = Array::get_as_ref(col_ndx.val + s_first_col_index);
1,086✔
1058
    set_string_interner(values, col_key);
1,086✔
1059
    values.init_from_ref(ref);
1,086✔
1060
    size_t sz = values.size();
1,086✔
1061
    for (size_t i = 0; i < sz; i++) {
118,542✔
1062
        auto v = values.get(i);
117,456✔
1063
        size_t pos = keys.lower_bound(v);
117,456✔
1064
        REALM_ASSERT_3(pos, !=, keys.size());
117,456✔
1065
        indexes.add(pos);
117,456✔
1066
    }
117,456✔
1067
    Array::set(col_ndx.val + s_first_col_index, indexes.get_ref());
1,086✔
1068
    Array::destroy_deep(ref, m_alloc);
1,086✔
1069
}
1,086✔
1070

1071
void Cluster::init_leaf(ColKey col_key, ArrayPayload* leaf) const
1072
{
8,776,041✔
1073
    auto col_ndx = col_key.get_index();
8,776,041✔
1074
    // FIXME: Move this validation into callers.
1075
    // Currently, the query subsystem may call with an unvalidated key.
1076
    // once fixed, reintroduce the noexcept declaration :-D
1077
    if (auto t = m_tree_top.get_owning_table())
8,776,041✔
1078
        t->check_column(col_key);
8,721,891✔
1079
    ref_type ref = to_ref(Array::get(col_ndx.val + 1));
8,776,041✔
1080
    if (leaf->need_string_interner()) {
8,776,041✔
1081
        m_tree_top.set_string_interner(*leaf, col_key);
154,488✔
1082
    }
154,488✔
1083
    if (leaf->need_spec()) {
8,776,041✔
1084
        m_tree_top.set_spec(*leaf, col_ndx);
142,110✔
1085
    }
142,110✔
1086
    leaf->init_from_ref(ref);
8,776,041✔
1087
    leaf->set_parent(const_cast<Cluster*>(this), col_ndx.val + 1);
8,776,041✔
1088
}
8,776,041✔
1089

1090
void Cluster::add_leaf(ColKey col_key, ref_type ref)
1091
{
×
1092
    auto col_ndx = col_key.get_index();
×
1093
    REALM_ASSERT((col_ndx.val + 1) == size());
×
1094
    Array::insert(col_ndx.val + 1, from_ref(ref));
×
1095
}
×
1096

1097
template <typename ArrayType>
1098
void Cluster::verify(ref_type ref, size_t index, util::Optional<size_t>& sz) const
1099
{
551,634✔
1100
    ArrayType arr(get_alloc());
551,634✔
1101
    set_spec(arr, ColKey::Idx{unsigned(index) - 1});
551,634✔
1102
    auto table = get_owning_table();
551,634✔
1103
    REALM_ASSERT(index <= table->m_leaf_ndx2colkey.size());
551,634✔
1104
    auto col_key = table->m_leaf_ndx2colkey[index - 1];
551,634✔
1105
    set_string_interner(arr, col_key);
551,634✔
1106
    arr.set_parent(const_cast<Cluster*>(this), index);
551,634✔
1107
    arr.init_from_ref(ref);
551,634✔
1108
    arr.verify();
551,634✔
1109
    if (sz) {
551,634✔
1110
        REALM_ASSERT(arr.size() == *sz);
195,939✔
1111
    }
195,939✔
1112
    else {
355,695✔
1113
        sz = arr.size();
355,695✔
1114
    }
355,695✔
1115
}
551,634✔
1116
namespace {
1117

1118
template <typename ArrayType>
1119
void verify_list(ArrayRef& arr, size_t sz)
1120
{
89,808✔
1121
    for (size_t n = 0; n < sz; n++) {
324,927!
1122
        if (ref_type bp_tree_ref = arr.get(n)) {
235,119!
1123
            BPlusTree<ArrayType> links(arr.get_alloc());
101,439✔
1124
            links.init_from_ref(bp_tree_ref);
101,439✔
1125
            links.set_parent(&arr, n);
101,439✔
1126
            links.verify();
101,439✔
1127
        }
101,439✔
1128
    }
235,119✔
1129
}
89,808✔
1130

1131
template <typename SetType>
1132
void verify_set(ArrayRef& arr, size_t sz)
1133
{
174✔
1134
    for (size_t n = 0; n < sz; ++n) {
402!
1135
        if (ref_type bp_tree_ref = arr.get(n)) {
228!
1136
            BPlusTree<SetType> elements(arr.get_alloc());
216✔
1137
            elements.init_from_ref(bp_tree_ref);
216✔
1138
            elements.set_parent(&arr, n);
216✔
1139
            elements.verify();
216✔
1140

1141
            // FIXME: Check uniqueness of elements.
1142
        }
216✔
1143
    }
228✔
1144
}
174✔
1145

1146
} // namespace
1147

1148
void Cluster::verify() const
1149
{
392,325✔
1150
#ifdef REALM_DEBUG
392,325✔
1151
    util::Optional<size_t> sz;
392,325✔
1152

1153
    auto verify_column = [this, &sz](ColKey col_key) {
653,889✔
1154
        size_t col = col_key.get_index().val + s_first_col_index;
653,889✔
1155
        ref_type ref = Array::get_as_ref(col);
653,889✔
1156
        auto attr = col_key.get_attrs();
653,889✔
1157
        auto col_type = col_key.get_type();
653,889✔
1158
        bool nullable = attr.test(col_attr_Nullable);
653,889✔
1159

1160
        if (attr.test(col_attr_List)) {
653,889✔
1161
            ArrayRef arr(get_alloc());
89,826✔
1162
            arr.set_parent(const_cast<Cluster*>(this), col);
89,826✔
1163
            arr.init_from_ref(ref);
89,826✔
1164
            arr.verify();
89,826✔
1165
            if (sz) {
89,826✔
1166
                REALM_ASSERT(arr.size() == *sz);
65,493✔
1167
            }
65,493✔
1168
            else {
24,333✔
1169
                sz = arr.size();
24,333✔
1170
            }
24,333✔
1171

1172
            switch (col_type) {
89,826✔
1173
                case col_type_Int:
38,355✔
1174
                    if (nullable) {
38,355✔
1175
                        verify_list<util::Optional<int64_t>>(arr, *sz);
×
1176
                    }
×
1177
                    else {
38,355✔
1178
                        verify_list<int64_t>(arr, *sz);
38,355✔
1179
                    }
38,355✔
1180
                    break;
38,355✔
1181
                case col_type_Bool:
✔
1182
                    verify_list<Bool>(arr, *sz);
×
1183
                    break;
×
1184
                case col_type_Float:
6✔
1185
                    verify_list<Float>(arr, *sz);
6✔
1186
                    break;
6✔
1187
                case col_type_Double:
✔
1188
                    verify_list<Double>(arr, *sz);
×
1189
                    break;
×
1190
                case col_type_String:
26,283✔
1191
                    verify_list<String>(arr, *sz);
26,283✔
1192
                    break;
26,283✔
1193
                case col_type_Binary:
24,000✔
1194
                    verify_list<Binary>(arr, *sz);
24,000✔
1195
                    break;
24,000✔
1196
                case col_type_Timestamp:
✔
1197
                    verify_list<Timestamp>(arr, *sz);
×
1198
                    break;
×
1199
                case col_type_Decimal:
✔
1200
                    verify_list<Decimal128>(arr, *sz);
×
1201
                    break;
×
1202
                case col_type_ObjectId:
✔
1203
                    verify_list<ObjectId>(arr, *sz);
×
1204
                    break;
×
1205
                case col_type_UUID:
✔
1206
                    verify_list<UUID>(arr, *sz);
×
1207
                    break;
×
1208
                case col_type_Link:
1,164✔
1209
                    verify_list<ObjKey>(arr, *sz);
1,164✔
1210
                    break;
1,164✔
1211
                default:
18✔
1212
                    // FIXME: Nullable primitives
1213
                    break;
18✔
1214
            }
89,826✔
1215
            return IteratorControl::AdvanceToNext;
89,826✔
1216
        }
89,826✔
1217
        else if (attr.test(col_attr_Dictionary)) {
564,063✔
1218
            ArrayRef arr(get_alloc());
12,168✔
1219
            arr.set_parent(const_cast<Cluster*>(this), col);
12,168✔
1220
            arr.init_from_ref(ref);
12,168✔
1221
            arr.verify();
12,168✔
1222
            if (sz) {
12,168✔
1223
                REALM_ASSERT(arr.size() == *sz);
138✔
1224
            }
138✔
1225
            else {
12,030✔
1226
                sz = arr.size();
12,030✔
1227
            }
12,030✔
1228
            for (size_t n = 0; n < sz; n++) {
24,504✔
1229
                if (auto ref = arr.get(n)) {
12,336✔
1230
                    Dictionary dict(get_alloc(), col_key, to_ref(ref));
12,219✔
1231
                    dict.verify();
12,219✔
1232
                }
12,219✔
1233
            }
12,336✔
1234
            return IteratorControl::AdvanceToNext;
12,168✔
1235
        }
12,168✔
1236
        else if (attr.test(col_attr_Set)) {
551,895✔
1237
            ArrayRef arr(get_alloc());
270✔
1238
            arr.set_parent(const_cast<Cluster*>(this), col);
270✔
1239
            arr.init_from_ref(ref);
270✔
1240
            arr.verify();
270✔
1241
            if (sz) {
270✔
1242
                REALM_ASSERT(arr.size() == *sz);
252✔
1243
            }
252✔
1244
            else {
18✔
1245
                sz = arr.size();
18✔
1246
            }
18✔
1247
            switch (col_type) {
270✔
1248
                case col_type_Int:
138✔
1249
                    if (nullable) {
138✔
1250
                        verify_set<util::Optional<int64_t>>(arr, *sz);
×
1251
                    }
×
1252
                    else {
138✔
1253
                        verify_set<int64_t>(arr, *sz);
138✔
1254
                    }
138✔
1255
                    break;
138✔
1256
                case col_type_Bool:
✔
1257
                    verify_set<Bool>(arr, *sz);
×
1258
                    break;
×
1259
                case col_type_Float:
✔
1260
                    verify_set<Float>(arr, *sz);
×
1261
                    break;
×
1262
                case col_type_Double:
✔
1263
                    verify_set<Double>(arr, *sz);
×
1264
                    break;
×
1265
                case col_type_String:
6✔
1266
                    verify_set<String>(arr, *sz);
6✔
1267
                    break;
6✔
1268
                case col_type_Binary:
18✔
1269
                    verify_set<Binary>(arr, *sz);
18✔
1270
                    break;
18✔
1271
                case col_type_Timestamp:
✔
1272
                    verify_set<Timestamp>(arr, *sz);
×
1273
                    break;
×
1274
                case col_type_Decimal:
✔
1275
                    verify_set<Decimal128>(arr, *sz);
×
1276
                    break;
×
1277
                case col_type_ObjectId:
✔
1278
                    verify_set<ObjectId>(arr, *sz);
×
1279
                    break;
×
1280
                case col_type_UUID:
✔
1281
                    verify_set<UUID>(arr, *sz);
×
1282
                    break;
×
1283
                case col_type_Link:
12✔
1284
                    verify_set<ObjKey>(arr, *sz);
12✔
1285
                    break;
12✔
1286
                default:
96✔
1287
                    // FIXME: Nullable primitives
1288
                    break;
96✔
1289
            }
270✔
1290
            return IteratorControl::AdvanceToNext;
270✔
1291
        }
270✔
1292

1293
        switch (col_type) {
551,625✔
1294
            case col_type_Int:
337,443✔
1295
                if (nullable) {
337,443✔
1296
                    verify<ArrayIntNull>(ref, col, sz);
35,604✔
1297
                }
35,604✔
1298
                else {
301,839✔
1299
                    verify<ArrayInteger>(ref, col, sz);
301,839✔
1300
                }
301,839✔
1301
                break;
337,443✔
1302
            case col_type_Bool:
7,224✔
1303
                verify<ArrayBoolNull>(ref, col, sz);
7,224✔
1304
                break;
7,224✔
1305
            case col_type_Float:
162✔
1306
                verify<ArrayFloatNull>(ref, col, sz);
162✔
1307
                break;
162✔
1308
            case col_type_Double:
156✔
1309
                verify<ArrayDoubleNull>(ref, col, sz);
156✔
1310
                break;
156✔
1311
            case col_type_String:
142,845✔
1312
                verify<ArrayString>(ref, col, sz);
142,845✔
1313
                break;
142,845✔
1314
            case col_type_Binary:
49,872✔
1315
                verify<ArrayBinary>(ref, col, sz);
49,872✔
1316
                break;
49,872✔
1317
            case col_type_Mixed:
2,724✔
1318
                verify<ArrayMixed>(ref, col, sz);
2,724✔
1319
                break;
2,724✔
1320
            case col_type_Timestamp:
6,948✔
1321
                verify<ArrayTimestamp>(ref, col, sz);
6,948✔
1322
                break;
6,948✔
1323
            case col_type_Decimal:
42✔
1324
                verify<ArrayDecimal128>(ref, col, sz);
42✔
1325
                break;
42✔
1326
            case col_type_ObjectId:
42✔
1327
                verify<ArrayObjectIdNull>(ref, col, sz);
42✔
1328
                break;
42✔
1329
            case col_type_UUID:
✔
1330
                verify<ArrayUUIDNull>(ref, col, sz);
×
1331
                break;
×
1332
            case col_type_Link:
1,575✔
1333
                verify<ArrayKey>(ref, col, sz);
1,575✔
1334
                break;
1,575✔
1335
            case col_type_BackLink:
2,598✔
1336
                verify<ArrayBacklink>(ref, col, sz);
2,598✔
1337
                break;
2,598✔
1338
            default:
✔
1339
                break;
×
1340
        }
551,625✔
1341
        return IteratorControl::AdvanceToNext;
551,622✔
1342
    };
551,625✔
1343

1344
    m_tree_top.m_owner->for_each_and_every_column(verify_column);
392,325✔
1345
#endif
392,325✔
1346
}
392,325✔
1347

1348
// LCOV_EXCL_START
1349
void Cluster::dump_objects(int64_t key_offset, std::string lead) const
1350
{
×
1351
    std::cout << lead << "leaf - size: " << node_size() << std::endl;
×
1352
    if (!m_keys.is_attached()) {
×
1353
        std::cout << lead << "compact form" << std::endl;
×
1354
    }
×
1355

1356
    for (unsigned i = 0; i < node_size(); i++) {
×
1357
        int64_t key_value;
×
1358
        if (m_keys.is_attached()) {
×
1359
            key_value = m_keys.get(i);
×
1360
        }
×
1361
        else {
×
1362
            key_value = int64_t(i);
×
1363
        }
×
1364
        std::cout << lead << "key: " << std::hex << key_value + key_offset << std::dec;
×
1365
        m_tree_top.m_owner->for_each_and_every_column([&](ColKey col) {
×
1366
            size_t j = col.get_index().val + 1;
×
1367
            if (col.get_attrs().test(col_attr_List)) {
×
1368
                ref_type ref = Array::get_as_ref(j);
×
1369
                ArrayRef refs(m_alloc);
×
1370
                refs.init_from_ref(ref);
×
1371
                std::cout << ", {";
×
1372
                ref = refs.get(i);
×
1373
                if (ref) {
×
1374
                    if (col.get_type() == col_type_Int) {
×
1375
                        // This is easy to handle
1376
                        Array ints(m_alloc);
×
1377
                        ints.init_from_ref(ref);
×
1378
                        for (size_t n = 0; n < ints.size(); n++) {
×
1379
                            std::cout << ints.get(n) << ", ";
×
1380
                        }
×
1381
                    }
×
1382
                    else {
×
1383
                        std::cout << col.get_type();
×
1384
                    }
×
1385
                }
×
1386
                std::cout << "}";
×
1387
                return IteratorControl::AdvanceToNext;
×
1388
            }
×
1389

1390
            switch (col.get_type()) {
×
1391
                case col_type_Int: {
×
1392
                    bool nullable = col.get_attrs().test(col_attr_Nullable);
×
1393
                    ref_type ref = Array::get_as_ref(j);
×
1394
                    if (nullable) {
×
1395
                        ArrayIntNull arr_int_null(m_alloc);
×
1396
                        arr_int_null.init_from_ref(ref);
×
1397
                        if (arr_int_null.is_null(i)) {
×
1398
                            std::cout << ", null";
×
1399
                        }
×
1400
                        else {
×
1401
                            std::cout << ", " << *arr_int_null.get(i);
×
1402
                        }
×
1403
                    }
×
1404
                    else {
×
1405
                        Array arr(m_alloc);
×
1406
                        arr.init_from_ref(ref);
×
1407
                        std::cout << ", " << arr.get(i);
×
1408
                    }
×
1409
                    break;
×
1410
                }
×
1411
                case col_type_Bool: {
×
1412
                    ArrayBoolNull arr(m_alloc);
×
1413
                    ref_type ref = Array::get_as_ref(j);
×
1414
                    arr.init_from_ref(ref);
×
1415
                    auto val = arr.get(i);
×
1416
                    std::cout << ", " << (val ? (*val ? "true" : "false") : "null");
×
1417
                    break;
×
1418
                }
×
1419
                case col_type_Float: {
×
1420
                    ArrayFloatNull arr(m_alloc);
×
1421
                    ref_type ref = Array::get_as_ref(j);
×
1422
                    arr.init_from_ref(ref);
×
1423
                    auto val = arr.get(i);
×
1424
                    if (val)
×
1425
                        std::cout << ", " << *val;
×
1426
                    else
×
1427
                        std::cout << ", null";
×
1428
                    break;
×
1429
                }
×
1430
                case col_type_Double: {
×
1431
                    ArrayDoubleNull arr(m_alloc);
×
1432
                    ref_type ref = Array::get_as_ref(j);
×
1433
                    arr.init_from_ref(ref);
×
1434
                    auto val = arr.get(i);
×
1435
                    if (val)
×
1436
                        std::cout << ", " << *val;
×
1437
                    else
×
1438
                        std::cout << ", null";
×
1439
                    break;
×
1440
                }
×
1441
                case col_type_String: {
×
1442
                    ArrayString arr(m_alloc);
×
1443
                    set_spec(arr, col.get_index());
×
NEW
1444
                    set_string_interner(arr, col);
×
1445
                    ref_type ref = Array::get_as_ref(j);
×
1446
                    arr.init_from_ref(ref);
×
1447
                    std::cout << ", " << arr.get(i);
×
1448
                    break;
×
1449
                }
×
1450
                case col_type_Binary: {
×
1451
                    ArrayBinary arr(m_alloc);
×
1452
                    ref_type ref = Array::get_as_ref(j);
×
1453
                    arr.init_from_ref(ref);
×
1454
                    std::cout << ", " << arr.get(i);
×
1455
                    break;
×
1456
                }
×
1457
                case col_type_Mixed: {
×
1458
                    ArrayMixed arr(m_alloc);
×
NEW
1459
                    set_string_interner(arr, col);
×
1460
                    ref_type ref = Array::get_as_ref(j);
×
1461
                    arr.init_from_ref(ref);
×
1462
                    std::cout << ", " << arr.get(i);
×
1463
                    break;
×
1464
                }
×
1465
                case col_type_Timestamp: {
×
1466
                    ArrayTimestamp arr(m_alloc);
×
1467
                    ref_type ref = Array::get_as_ref(j);
×
1468
                    arr.init_from_ref(ref);
×
1469
                    if (arr.is_null(i)) {
×
1470
                        std::cout << ", null";
×
1471
                    }
×
1472
                    else {
×
1473
                        std::cout << ", " << arr.get(i);
×
1474
                    }
×
1475
                    break;
×
1476
                }
×
1477
                case col_type_Decimal: {
×
1478
                    ArrayDecimal128 arr(m_alloc);
×
1479
                    ref_type ref = Array::get_as_ref(j);
×
1480
                    arr.init_from_ref(ref);
×
1481
                    if (arr.is_null(i)) {
×
1482
                        std::cout << ", null";
×
1483
                    }
×
1484
                    else {
×
1485
                        std::cout << ", " << arr.get(i);
×
1486
                    }
×
1487
                    break;
×
1488
                }
×
1489
                case col_type_ObjectId: {
×
1490
                    ArrayObjectIdNull arr(m_alloc);
×
1491
                    ref_type ref = Array::get_as_ref(j);
×
1492
                    arr.init_from_ref(ref);
×
1493
                    if (arr.is_null(i)) {
×
1494
                        std::cout << ", null";
×
1495
                    }
×
1496
                    else {
×
1497
                        std::cout << ", " << *arr.get(i);
×
1498
                    }
×
1499
                    break;
×
1500
                }
×
1501
                case col_type_UUID: {
×
1502
                    ArrayUUIDNull arr(m_alloc);
×
1503
                    ref_type ref = Array::get_as_ref(j);
×
1504
                    arr.init_from_ref(ref);
×
1505
                    if (arr.is_null(i)) {
×
1506
                        std::cout << ", null";
×
1507
                    }
×
1508
                    else {
×
1509
                        std::cout << ", " << arr.get(i);
×
1510
                    }
×
1511
                    break;
×
1512
                }
×
1513
                case col_type_Link: {
×
1514
                    ArrayKey arr(m_alloc);
×
1515
                    ref_type ref = Array::get_as_ref(j);
×
1516
                    arr.init_from_ref(ref);
×
1517
                    std::cout << ", " << arr.get(i);
×
1518
                    break;
×
1519
                }
×
1520
                case col_type_BackLink: {
×
1521
                    break;
×
1522
                }
×
1523
                default:
×
1524
                    std::cout << ", Error";
×
1525
                    break;
×
1526
            }
×
1527
            return IteratorControl::AdvanceToNext;
×
1528
        });
×
1529
        std::cout << std::endl;
×
1530
    }
×
1531
}
×
1532
// LCOV_EXCL_STOP
1533

1534
void Cluster::remove_backlinks(const Table* origin_table, ObjKey origin_key, ColKey origin_col_key,
1535
                               const std::vector<ObjKey>& keys, CascadeState& state)
1536
{
91,119✔
1537
    TableRef target_table = origin_table->get_opposite_table(origin_col_key);
91,119✔
1538
    ColKey backlink_col_key = origin_table->get_opposite_column(origin_col_key);
91,119✔
1539
    bool strong_links = target_table->is_embedded();
91,119✔
1540

1541
    for (auto key : keys) {
92,757✔
1542
        if (key != null_key) {
92,757✔
1543
            bool is_unres = key.is_unresolved();
92,757✔
1544
            Obj target_obj = is_unres ? target_table->m_tombstones->get(key) : target_table->m_clusters.get(key);
92,757✔
1545
            bool last_removed = target_obj.remove_one_backlink(backlink_col_key, origin_key); // Throws
92,757✔
1546
            if (is_unres) {
92,757✔
1547
                if (last_removed) {
30✔
1548
                    // Check is there are more backlinks
1549
                    if (!target_obj.has_backlinks(false)) {
24✔
1550
                        // Tombstones can be erased right away - there is no cascading effect
1551
                        target_table->m_tombstones->erase(key, state);
18✔
1552
                    }
18✔
1553
                }
24✔
1554
            }
30✔
1555
            else {
92,727✔
1556
                state.enqueue_for_cascade(target_obj, strong_links, last_removed);
92,727✔
1557
            }
92,727✔
1558
        }
92,757✔
1559
    }
92,757✔
1560
}
91,119✔
1561

1562
void Cluster::remove_backlinks(const Table* origin_table, ObjKey origin_key, ColKey origin_col_key,
1563
                               const std::vector<ObjLink>& links, CascadeState& state)
1564
{
144✔
1565
    Group* group = origin_table->get_parent_group();
144✔
1566
    TableKey origin_table_key = origin_table->get_key();
144✔
1567

1568
    for (auto link : links) {
240✔
1569
        if (link) {
240✔
1570
            bool is_unres = link.get_obj_key().is_unresolved();
240✔
1571
            Obj target_obj = group->get_object(link);
240✔
1572
            TableRef target_table = target_obj.get_table();
240✔
1573
            ColKey backlink_col_key = target_table->find_or_add_backlink_column(origin_col_key, origin_table_key);
240✔
1574

1575
            bool last_removed = target_obj.remove_one_backlink(backlink_col_key, origin_key); // Throws
240✔
1576
            if (is_unres) {
240✔
1577
                if (last_removed) {
6✔
1578
                    // Check if there are more backlinks
1579
                    if (!target_obj.has_backlinks(false)) {
6✔
1580
                        // Tombstones can be erased right away - there is no cascading effect
1581
                        target_table->m_tombstones->erase(link.get_obj_key(), state);
6✔
1582
                    }
6✔
1583
                }
6✔
1584
            }
6✔
1585
            else {
234✔
1586
                state.enqueue_for_cascade(target_obj, false, last_removed);
234✔
1587
            }
234✔
1588
        }
240✔
1589
    }
240✔
1590
}
144✔
1591

1592
namespace {
1593

1594
template <typename Fn>
1595
static void switch_on_type(ColKey ck, Fn&& fn)
1596
{
1,581,924✔
1597
    bool is_optional = ck.is_nullable();
1,581,924✔
1598
    auto type = ck.get_type();
1,581,924✔
1599
    switch (type) {
1,581,924✔
1600
        case col_type_Int:
815,919✔
1601
            return is_optional ? fn((util::Optional<int64_t>*)0) : fn((int64_t*)0);
815,919✔
1602
        case col_type_Bool:
9,324✔
1603
            return is_optional ? fn((util::Optional<bool>*)0) : fn((bool*)0);
9,324✔
1604
        case col_type_Float:
9,519✔
1605
            return is_optional ? fn((util::Optional<float>*)0) : fn((float*)0);
9,519✔
1606
        case col_type_Double:
13,524✔
1607
            return is_optional ? fn((util::Optional<double>*)0) : fn((double*)0);
13,524✔
1608
        case col_type_String:
266,637✔
1609
            return fn((StringData*)0);
266,637✔
1610
        case col_type_Binary:
248,235✔
1611
            return fn((BinaryData*)0);
248,235✔
1612
        case col_type_Timestamp:
39,327✔
1613
            return fn((Timestamp*)0);
39,327✔
1614
        case col_type_Link:
56,175✔
1615
            return fn((ObjKey*)0);
56,175✔
1616
        case col_type_ObjectId:
86,874✔
1617
            return is_optional ? fn((util::Optional<ObjectId>*)0) : fn((ObjectId*)0);
86,874✔
1618
        case col_type_Decimal:
8,559✔
1619
            return fn((Decimal128*)0);
8,559✔
1620
        case col_type_UUID:
9,339✔
1621
            return is_optional ? fn((util::Optional<UUID>*)0) : fn((UUID*)0);
9,339✔
1622
        case col_type_Mixed:
18,519✔
1623
            return fn((Mixed*)0);
18,519✔
1624
        default:
✔
1625
            REALM_COMPILER_HINT_UNREACHABLE();
×
1626
    }
1,581,924✔
1627
}
1,581,924✔
1628

1629
} // namespace
1630

1631
ref_type Cluster::typed_write(ref_type ref, _impl::ArrayWriterBase& out) const
1632
{
1,072,548✔
1633
    REALM_ASSERT_DEBUG(ref == get_mem().get_ref());
1,072,548✔
1634
    bool only_modified = out.only_modified;
1,072,548✔
1635
    if (only_modified && m_alloc.is_read_only(ref))
1,072,548✔
1636
        return ref;
1,383✔
1637
    REALM_ASSERT_DEBUG(!get_is_inner_bptree_node_from_header(get_header()));
1,071,165✔
1638
    REALM_ASSERT_DEBUG(!get_context_flag_from_header(get_header()));
1,071,165✔
1639
    TempArray written_cluster(size());
1,071,165✔
1640
    for (size_t j = 0; j < size(); ++j) {
4,676,850✔
1641
        RefOrTagged leaf_rot = get_as_ref_or_tagged(j);
3,605,691✔
1642
        // Handle nulls
1643
        if (!leaf_rot.is_ref() || !leaf_rot.get_as_ref()) {
3,605,691✔
1644
            written_cluster.set(j, leaf_rot);
1,010,919✔
1645
            continue;
1,010,919✔
1646
        }
1,010,919✔
1647
        // prune subtrees which should not be written:
1648
        if (only_modified && m_alloc.is_read_only(leaf_rot.get_as_ref())) {
2,594,772✔
1649
            written_cluster.set(j, leaf_rot);
340,674✔
1650
            continue;
340,674✔
1651
        }
340,674✔
1652
        // from here: this leaf exists and needs to be written.
1653
        ref = leaf_rot.get_as_ref();
2,254,098✔
1654
        ref_type new_ref = ref;
2,254,098✔
1655
        if (j == 0) {
2,254,098✔
1656
            // Keys  (ArrayUnsigned me thinks, so don't compress)
1657
            Array leaf(m_alloc);
49,275✔
1658
            leaf.init_from_ref(ref);
49,275✔
1659
            new_ref = leaf.write(out, false, only_modified, false);
49,275✔
1660
        }
49,275✔
1661
        else {
2,204,823✔
1662
            // Columns
1663
            auto col_key = out.table->m_leaf_ndx2colkey[j - 1];
2,204,823✔
1664
            out.col_key = col_key;
2,204,823✔
1665
            auto col_type = col_key.get_type();
2,204,823✔
1666
            if (col_key.is_collection()) {
2,204,823✔
1667
                ArrayRef arr_ref(m_alloc);
450,375✔
1668
                arr_ref.init_from_ref(ref);
450,375✔
1669
                auto sz = arr_ref.size();
450,375✔
1670
                TempArray written_ref_leaf(sz);
450,375✔
1671

1672
                for (size_t k = 0; k < sz; k++) {
4,770,951✔
1673
                    ref_type new_sub_ref = 0;
4,320,582✔
1674
                    // Now we have to find out if the nested collection is a
1675
                    // dictionary or a list. If the top array has a size of 2
1676
                    // and it is not a BplusTree inner node, then it is a dictionary
1677
                    if (auto sub_ref = arr_ref.get(k)) {
4,320,582✔
1678
                        if (col_key.is_dictionary()) {
2,580,489✔
1679
                            new_sub_ref = Dictionary::typed_write(sub_ref, out, m_alloc);
43,806✔
1680
                        }
43,806✔
1681
                        else {
2,536,683✔
1682
                            // List or set - Can be handled the same way
1683
                            // For some reason, switch_on_type() would not compile on Windows
1684
                            // switch_on_type(col_key, [&](auto t) {
1685
                            //     using U = std::decay_t<decltype(*t)>;
1686
                            //     new_sub_ref = BPlusTree<U>::typed_write(sub_ref, out, m_alloc);
1687
                            // });
1688
                            switch (col_type) {
2,536,683✔
1689
                                case col_type_Int:
23,829✔
1690
                                    new_sub_ref = BPlusTree<int64_t>::typed_write(sub_ref, out, m_alloc);
23,829✔
1691
                                    break;
23,829✔
1692
                                case col_type_Bool:
2,124✔
1693
                                    new_sub_ref = BPlusTree<bool>::typed_write(sub_ref, out, m_alloc);
2,124✔
1694
                                    break;
2,124✔
1695
                                case col_type_Float:
2,208✔
1696
                                    new_sub_ref = BPlusTree<float>::typed_write(sub_ref, out, m_alloc);
2,208✔
1697
                                    break;
2,208✔
1698
                                case col_type_Double:
2,406✔
1699
                                    new_sub_ref = BPlusTree<double>::typed_write(sub_ref, out, m_alloc);
2,406✔
1700
                                    break;
2,406✔
1701
                                case col_type_String:
8,625✔
1702
                                    new_sub_ref = BPlusTree<StringData>::typed_write(sub_ref, out, m_alloc);
8,625✔
1703
                                    break;
8,625✔
1704
                                case col_type_Binary:
19,749✔
1705
                                    new_sub_ref = BPlusTree<BinaryData>::typed_write(sub_ref, out, m_alloc);
19,749✔
1706
                                    break;
19,749✔
1707
                                case col_type_Timestamp:
2,154✔
1708
                                    new_sub_ref = BPlusTree<Timestamp>::typed_write(sub_ref, out, m_alloc);
2,154✔
1709
                                    break;
2,154✔
1710
                                case col_type_Link:
2,450,685✔
1711
                                    new_sub_ref = BPlusTree<ObjKey>::typed_write(sub_ref, out, m_alloc);
2,450,685✔
1712
                                    break;
2,450,685✔
1713
                                case col_type_ObjectId:
13,512✔
1714
                                    new_sub_ref = BPlusTree<ObjectId>::typed_write(sub_ref, out, m_alloc);
13,512✔
1715
                                    break;
13,512✔
1716
                                case col_type_Decimal:
2,328✔
1717
                                    new_sub_ref = BPlusTree<Decimal128>::typed_write(sub_ref, out, m_alloc);
2,328✔
1718
                                    break;
2,328✔
1719
                                case col_type_UUID:
2,148✔
1720
                                    new_sub_ref = BPlusTree<UUID>::typed_write(sub_ref, out, m_alloc);
2,148✔
1721
                                    break;
2,148✔
1722
                                case col_type_Mixed:
6,912✔
1723
                                    new_sub_ref = BPlusTree<Mixed>::typed_write(sub_ref, out, m_alloc);
6,912✔
1724
                                    break;
6,912✔
1725
                                default:
✔
1726
                                    REALM_COMPILER_HINT_UNREACHABLE();
×
1727
                            }
2,536,683✔
1728
                        }
2,536,683✔
1729
                    }
2,580,489✔
1730
                    written_ref_leaf.set_as_ref(k, new_sub_ref);
4,320,576✔
1731
                }
4,320,576✔
1732
                new_ref = written_ref_leaf.write(out);
450,369✔
1733
            }
450,369✔
1734
            else if (col_type == col_type_BackLink) {
1,754,448✔
1735
                Array leaf(m_alloc);
172,599✔
1736
                leaf.init_from_ref(ref);
172,599✔
1737
                new_ref = leaf.write(out, true, only_modified, false);
172,599✔
1738
            }
172,599✔
1739
            else {
1,581,849✔
1740
                switch_on_type(col_key, [&](auto t) {
1,581,945✔
1741
                    using U = std::decay_t<decltype(*t)>;
1,581,945✔
1742
                    new_ref = ColumnTypeTraits<U>::cluster_leaf_type::typed_write(ref, out, m_alloc);
1,581,945✔
1743
                });
1,581,945✔
1744
            }
1,581,849✔
1745
        }
2,204,823✔
1746
        written_cluster.set_as_ref(j, new_ref);
2,254,092✔
1747
    }
2,254,092✔
1748
    return written_cluster.write(out);
1,071,159✔
1749
}
1,071,165✔
1750

1751
void Cluster::typed_print(std::string prefix) const
1752
{
×
1753
    REALM_ASSERT_DEBUG(!get_is_inner_bptree_node_from_header(get_header()));
×
1754
    std::cout << "Cluster of size " << size() << " " << header_to_string(get_header()) << std::endl;
×
1755
    const auto table = get_owning_table();
×
1756
    for (unsigned j = 0; j < size(); ++j) {
×
1757
        RefOrTagged rot = get_as_ref_or_tagged(j);
×
1758
        auto pref = prefix + "  " + std::to_string(j) + ":\t";
×
1759
        if (rot.is_ref() && rot.get_as_ref()) {
×
1760
            if (j == 0) {
×
1761
                std::cout << pref << "Keys as ArrayUnsigned as ";
×
1762
                Array a(m_alloc);
×
1763
                a.init_from_ref(rot.get_as_ref());
×
1764
                a.typed_print(pref);
×
1765
            }
×
1766
            else {
×
1767
                auto col_key = table->m_leaf_ndx2colkey[j - 1];
×
1768
                auto col_type = col_key.get_type();
×
1769
                auto col_attr = col_key.get_attrs();
×
1770
                std::string attr_string;
×
1771
                if (col_attr.test(col_attr_Dictionary))
×
1772
                    attr_string = "Dict:";
×
1773
                if (col_attr.test(col_attr_List))
×
1774
                    attr_string = "List:";
×
1775
                if (col_attr.test(col_attr_Set))
×
1776
                    attr_string = "Set:";
×
1777
                if (col_attr.test(col_attr_Nullable))
×
1778
                    attr_string += "Null:";
×
1779
                std::cout << pref << "Column[" << attr_string << col_type << "] as ";
×
1780
                // special cases for the types we want to compress
1781
                if (col_attr.test(col_attr_List) || col_attr.test(col_attr_Set)) {
×
1782
                    // That is a single bplustree
1783
                    // propagation of nullable missing here?
1784
                    // handling of mixed missing here?
1785
                    BPlusTreeBase::typed_print(pref, m_alloc, rot.get_as_ref(), col_type);
×
1786
                }
×
1787
                else if (col_attr.test(col_attr_Dictionary)) {
×
1788
                    Array dict_top(m_alloc);
×
1789
                    dict_top.init_from_ref(rot.get_as_ref());
×
1790
                    if (dict_top.size() == 0) {
×
1791
                        std::cout << "{ empty }" << std::endl;
×
1792
                        continue;
×
1793
                    }
×
1794
                    std::cout << "{" << std::endl;
×
1795
                    auto ref0 = dict_top.get_as_ref(0);
×
1796
                    if (ref0) {
×
1797
                        auto p = pref + "  0:\t";
×
1798
                        std::cout << p;
×
1799
                        BPlusTreeBase::typed_print(p, m_alloc, ref0, col_type);
×
1800
                    }
×
1801
                    if (dict_top.size() == 1) {
×
1802
                        continue; // is this really possible? or should all dicts have both trees?
×
1803
                    }
×
1804
                    auto ref1 = dict_top.get_as_ref(1);
×
1805
                    if (ref1) {
×
1806
                        auto p = pref + "  1:\t";
×
1807
                        std::cout << p;
×
1808
                        BPlusTreeBase::typed_print(p, m_alloc, dict_top.get_as_ref(1), col_type);
×
1809
                    }
×
1810
                }
×
1811
                else {
×
1812
                    // handle all other cases as generic arrays
1813
                    Array a(m_alloc);
×
1814
                    a.init_from_ref(rot.get_as_ref());
×
1815
                    a.typed_print(pref);
×
1816
                }
×
1817
            }
×
1818
        }
×
1819
    }
×
1820
}
×
1821

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