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

realm / realm-core / jorgen.edelbo_389

12 Aug 2024 02:13PM UTC coverage: 91.085% (-0.02%) from 91.107%
jorgen.edelbo_389

Pull #7826

Evergreen

jedelbo
Bump file format version
Pull Request #7826: Merge Next major

103458 of 182206 branches covered (56.78%)

3138 of 3500 new or added lines in 53 files covered. (89.66%)

175 existing lines in 17 files now uncovered.

219944 of 241471 relevant lines covered (91.09%)

6840929.52 hits per line

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

89.6
/src/realm/cluster_tree.cpp
1
/*************************************************************************
2
 *
3
 * Copyright 2020 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_tree.hpp"
20
#include "realm/group.hpp"
21
#include "realm/replication.hpp"
22
#include "realm/array_key.hpp"
23
#include "realm/array_integer.hpp"
24
#include "realm/array_backlink.hpp"
25
#include "realm/array_typed_link.hpp"
26
#include "realm/array_timestamp.hpp"
27
#include "realm/array_bool.hpp"
28
#include "realm/array_string.hpp"
29
#include "realm/array_mixed.hpp"
30
#include "realm/array_fixed_bytes.hpp"
31

32
#include <iostream>
33

34
/*
35
 * Node-splitting is done in the way that if the new element comes after all the
36
 * current elements, then the new element is added to the new node as the only
37
 * element and the old node is untouched. Here split key is the key of the new
38
 * element.
39
 * Otherwise the node is split so that the new element can be added to the old node.
40
 * So all elements that should come after the new element are moved to the new node.
41
 * Split key is the key of the first element that is moved. (First key that comes
42
 * after the new element).
43
 * Merging is done when a node is less than half full and the combined size will be
44
 * less than 3/4 of the max size.
45
 */
46

47
namespace realm {
48
/*
49
 * The inner nodes are organized in the way that the main array has a ref to the
50
 * (optional) key array in position 0 and the subtree depth in position 1. After
51
 * that follows refs to the subordinate nodes.
52
 */
53
class ClusterNodeInner : public ClusterNode {
54
public:
55
    ClusterNodeInner(Allocator& allocator, const ClusterTree& tree_top);
56
    ~ClusterNodeInner() override;
57

58
    void create(int sub_tree_depth);
59
    void init(MemRef mem) override;
60
    void update_from_parent() noexcept override;
61
    MemRef ensure_writeable(RowKey k) override;
62
    void update_ref_in_parent(RowKey, ref_type) override;
63

64
    bool is_leaf() const override
65
    {
48,412,389✔
66
        return false;
48,412,389✔
67
    }
48,412,389✔
68

69
    int get_sub_tree_depth() const override
70
    {
66✔
71
        return m_sub_tree_depth;
66✔
72
    }
66✔
73

74
    bool traverse(ClusterTree::TraverseFunction func, int64_t) const;
75
    void update(ClusterTree::UpdateFunction func, int64_t);
76

77
    size_t node_size() const override
78
    {
84,650,481✔
79
        return Array::size() - s_first_node_index;
84,650,481✔
80
    }
84,650,481✔
81
    size_t get_tree_size() const override
82
    {
44,771,589✔
83
        return size_t(Array::get(s_sub_tree_size)) >> 1;
44,771,589✔
84
    }
44,771,589✔
85
    void set_tree_size(size_t sub_tree_size)
86
    {
42,030,075✔
87
        Array::set(s_sub_tree_size, sub_tree_size << 1 | 1);
42,030,075✔
88
    }
42,030,075✔
89
    size_t update_sub_tree_size();
90

91
    int64_t get_last_key_value() const override;
92

93
    void ensure_general_form() override;
94
    void insert_column(ColKey col) override;
95
    void remove_column(ColKey col) override;
96
    size_t nb_columns() const override;
97
    ref_type insert(RowKey k, const FieldValues& init_values, State& state) override;
98
    bool try_get(RowKey k, State& state) const noexcept override;
99
    ObjKey get(size_t ndx, State& state) const override;
100
    size_t get_ndx(RowKey key, size_t ndx) const noexcept override;
101
    size_t erase(RowKey k, CascadeState& state) override;
102
    void nullify_incoming_links(RowKey key, CascadeState& state) override;
103
    void add(ref_type ref, int64_t key_value = 0);
104

105
    // Reset first (and only!) child ref and return node based on the previous value
106
    std::unique_ptr<ClusterNode> return_and_clear_first_child()
107
    {
240✔
108
        REALM_ASSERT(node_size() == 1);
240✔
109
        auto new_root = m_tree_top.get_node(this, s_first_node_index);
240✔
110
        Array::set(s_first_node_index, 0); // The node is no longer belonging to this
240✔
111
        return new_root;
240✔
112
    }
240✔
113

114
    int64_t get_first_key_value()
115
    {
240✔
116
        return m_keys.is_attached() ? m_keys.get(0) : 0;
240✔
117
    }
240✔
118

119
    bool get_leaf(RowKey key, ClusterNode::IteratorState& state) const noexcept;
120

121
    void dump_objects(int64_t key_offset, std::string lead) const override;
122

123
    virtual ref_type typed_write(ref_type ref, _impl::ArrayWriterBase& out) const override
124
    {
104,316✔
125
        REALM_ASSERT_DEBUG(ref == get_mem().get_ref());
104,316✔
126
        if (out.only_modified && m_alloc.is_read_only(ref)) {
104,316✔
NEW
127
            return ref;
×
NEW
128
        }
×
129
        REALM_ASSERT_DEBUG(get_is_inner_bptree_node_from_header(get_header()));
104,316✔
130
        REALM_ASSERT_DEBUG(!get_context_flag_from_header(get_header()));
104,316✔
131
        REALM_ASSERT_DEBUG(has_refs());
104,316✔
132
        TempArray written_node(size(), type_InnerBptreeNode);
104,316✔
133
        for (unsigned j = 0; j < size(); ++j) {
2,638,683✔
134
            RefOrTagged rot = get_as_ref_or_tagged(j);
2,534,367✔
135
            if (rot.is_ref() && rot.get_as_ref()) {
2,534,367✔
136
                if (out.only_modified && m_alloc.is_read_only(rot.get_as_ref())) {
2,302,455✔
137
                    written_node.set(j, rot);
1,922,910✔
138
                    continue;
1,922,910✔
139
                }
1,922,910✔
140
                if (j == 0) {
379,545✔
141
                    // keys (ArrayUnsigned, me thinks)
142
                    Array array_unsigned(m_alloc);
1,026✔
143
                    array_unsigned.init_from_ref(rot.get_as_ref());
1,026✔
144
                    written_node.set_as_ref(j, array_unsigned.write(out, false, out.only_modified, false));
1,026✔
145
                }
1,026✔
146
                else {
378,519✔
147
                    auto header = m_alloc.translate(rot.get_as_ref());
378,519✔
148
                    MemRef m(header, rot.get_as_ref(), m_alloc);
378,519✔
149
                    if (get_is_inner_bptree_node_from_header(header)) {
378,519✔
150
                        ClusterNodeInner inner_node(m_alloc, m_tree_top);
7,458✔
151
                        inner_node.init(m);
7,458✔
152
                        written_node.set_as_ref(j, inner_node.typed_write(rot.get_as_ref(), out));
7,458✔
153
                    }
7,458✔
154
                    else {
371,061✔
155
                        Cluster cluster(j, m_alloc, m_tree_top);
371,061✔
156
                        cluster.init(m);
371,061✔
157
                        written_node.set_as_ref(j, cluster.typed_write(rot.get_as_ref(), out));
371,061✔
158
                    }
371,061✔
159
                }
378,519✔
160
            }
379,545✔
161
            else { // not a ref, just copy value over
231,912✔
162
                written_node.set(j, rot);
231,912✔
163
            }
231,912✔
164
        }
2,534,367✔
165
        return written_node.write(out);
104,316✔
166
    }
104,316✔
167

168
private:
169
    static constexpr size_t s_key_ref_index = 0;
170
    static constexpr size_t s_sub_tree_depth_index = 1;
171
    static constexpr size_t s_sub_tree_size = 2;
172
    static constexpr size_t s_first_node_index = 3;
173

174
    int m_sub_tree_depth = 0;
175
    int m_shift_factor = 0;
176

177
    struct ChildInfo {
178
        size_t ndx;
179
        uint64_t offset;
180
        RowKey key;
181
        MemRef mem;
182
    };
183
    bool find_child(RowKey key, ChildInfo& ret) const noexcept
184
    {
393,607,281✔
185
        if (m_keys.is_attached()) {
393,607,281✔
186
            auto upper = m_keys.upper_bound(key.value);
338,187,264✔
187
            // The first entry in m_keys will always be smaller than or equal
188
            // to all keys in this subtree. If you get zero returned here, the
189
            // key is not in the tree
190
            if (upper == 0) {
338,187,264✔
191
                return false;
×
192
            }
×
193
            ret.ndx = upper - 1;
338,187,264✔
194
            ret.offset = m_keys.get(ret.ndx);
338,187,264✔
195
        }
338,187,264✔
196
        else {
55,420,017✔
197
            size_t sz = node_size();
55,420,017✔
198
            REALM_ASSERT_DEBUG(sz > 0);
55,420,017✔
199
            size_t max_ndx = sz - 1;
55,420,017✔
200
            ret.ndx = std::min(size_t(key.value >> m_shift_factor), max_ndx);
55,420,017✔
201
            ret.offset = ret.ndx << m_shift_factor;
55,420,017✔
202
        }
55,420,017✔
203
        ret.key = RowKey(key.value - ret.offset);
393,607,281✔
204
        ref_type child_ref = _get_child_ref(ret.ndx);
393,607,281✔
205
        char* child_header = m_alloc.translate(child_ref);
393,607,281✔
206
        ret.mem = MemRef(child_header, child_ref, m_alloc);
393,607,281✔
207
        return true;
393,607,281✔
208
    }
393,607,281✔
209

210
    ref_type _get_child_ref(size_t ndx) const noexcept
211
    {
473,428,899✔
212
        return Array::get_as_ref(ndx + s_first_node_index);
473,428,899✔
213
    }
473,428,899✔
214
    void _insert_child_ref(size_t ndx, ref_type ref)
215
    {
92,532✔
216
        Array::insert(ndx + s_first_node_index, from_ref(ref));
92,532✔
217
    }
92,532✔
218
    void _erase_child_ref(size_t ndx)
219
    {
21,105✔
220
        Array::erase(ndx + s_first_node_index);
21,105✔
221
    }
21,105✔
222
    void move(size_t ndx, ClusterNode* new_node, int64_t key_adj) override;
223

224
    template <class T, class F>
225
    T recurse(RowKey key, F func);
226

227
    template <class T, class F>
228
    T recurse(ChildInfo& child_info, F func);
229
    // Adjust key offset values by adding offset
230
    void adjust_keys(int64_t offset);
231
    // Make sure the first key offset value is 0
232
    // This is done by adjusting the child node by current first offset
233
    // and setting it to 0 thereafter
234
    void adjust_keys_first_child(int64_t adj);
235
};
236

237
/***************************** ClusterNodeInner ******************************/
238

239
ClusterNodeInner::ClusterNodeInner(Allocator& allocator, const ClusterTree& tree_top)
240
    : ClusterNode(0, allocator, tree_top)
48,874,425✔
241
{
97,734,801✔
242
}
97,734,801✔
243

244
ClusterNodeInner::~ClusterNodeInner() {}
97,736,658✔
245

246
void ClusterNodeInner::create(int sub_tree_depth)
247
{
3,534✔
248
    Array::create(Array::type_InnerBptreeNode, false, s_first_node_index);
3,534✔
249

250
    Array::set(s_key_ref_index, 0);
3,534✔
251

252
    Array::set(s_sub_tree_depth_index, RefOrTagged::make_tagged(sub_tree_depth));
3,534✔
253
    Array::set(s_sub_tree_size, 1); // sub_tree_size = 0 (as tagged value)
3,534✔
254
    m_sub_tree_depth = sub_tree_depth;
3,534✔
255
    m_shift_factor = m_sub_tree_depth * node_shift_factor;
3,534✔
256
}
3,534✔
257

258
void ClusterNodeInner::init(MemRef mem)
259
{
95,655,828✔
260
    Array::init_from_mem(mem);
95,655,828✔
261
    m_keys.set_parent(this, s_key_ref_index);
95,655,828✔
262
    ref_type ref = Array::get_as_ref(s_key_ref_index);
95,655,828✔
263
    if (ref) {
95,655,828✔
264
        m_keys.init_from_ref(ref);
81,619,494✔
265
    }
81,619,494✔
266
    else {
14,036,334✔
267
        m_keys.detach();
14,036,334✔
268
    }
14,036,334✔
269
    m_sub_tree_depth = int(Array::get(s_sub_tree_depth_index)) >> 1;
95,655,828✔
270
    m_shift_factor = m_sub_tree_depth * node_shift_factor;
95,655,828✔
271
}
95,655,828✔
272

273
void ClusterNodeInner::update_from_parent() noexcept
274
{
86,508✔
275
    Array::update_from_parent();
86,508✔
276
    ref_type ref = Array::get_as_ref(s_key_ref_index);
86,508✔
277
    if (ref) {
86,508✔
278
        m_keys.update_from_parent();
74,058✔
279
    }
74,058✔
280
    m_sub_tree_depth = int(Array::get(s_sub_tree_depth_index)) >> 1;
86,508✔
281
}
86,508✔
282

283
template <class T, class F>
284
T ClusterNodeInner::recurse(RowKey key, F func)
285
{
50,506,836✔
286
    ChildInfo child_info;
50,506,836✔
287
    if (!find_child(key, child_info)) {
50,506,836✔
288
        throw KeyNotFound("Child not found in recurse");
×
289
    }
×
290
    return recurse<T>(child_info, func);
50,506,836✔
291
}
50,506,836✔
292

293
template <class T, class F>
294
T ClusterNodeInner::recurse(ChildInfo& child_info, F func)
295
{
392,027,520✔
296
    bool child_is_leaf = !Array::get_is_inner_bptree_node_from_header(child_info.mem.get_addr());
392,027,520✔
297
    if (child_is_leaf) {
392,027,520✔
298
        Cluster leaf(child_info.offset + m_offset, m_alloc, m_tree_top);
302,519,790✔
299
        leaf.set_parent(this, child_info.ndx + s_first_node_index);
302,519,790✔
300
        leaf.init(child_info.mem);
302,519,790✔
301
        return func(&leaf, child_info);
302,519,790✔
302
    }
302,519,790✔
303
    else {
89,507,730✔
304
        ClusterNodeInner node(m_alloc, m_tree_top);
89,507,730✔
305
        node.set_parent(this, child_info.ndx + s_first_node_index);
89,507,730✔
306
        node.init(child_info.mem);
89,507,730✔
307
        node.set_offset(child_info.offset + m_offset);
89,507,730✔
308
        return func(&node, child_info);
89,507,730✔
309
    }
89,507,730✔
310
}
392,027,520✔
311

312
MemRef ClusterNodeInner::ensure_writeable(RowKey key)
313
{
×
314
    return recurse<MemRef>(key, [](ClusterNode* node, ChildInfo& child_info) {
×
315
        return node->ensure_writeable(child_info.key);
×
316
    });
×
317
}
×
318

319
void ClusterNodeInner::update_ref_in_parent(RowKey key, ref_type ref)
320
{
486,858✔
321
    ChildInfo child_info;
486,858✔
322
    if (!find_child(key, child_info)) {
486,858✔
323
        throw KeyNotFound("Child not found in update_ref_in_parent");
×
324
    }
×
325
    if (this->m_sub_tree_depth == 1) {
486,858✔
326
        set(child_info.ndx + s_first_node_index, ref);
270,027✔
327
    }
270,027✔
328
    else {
216,831✔
329
        ClusterNodeInner node(m_alloc, m_tree_top);
216,831✔
330
        node.set_parent(this, child_info.ndx + s_first_node_index);
216,831✔
331
        node.init(child_info.mem);
216,831✔
332
        node.set_offset(child_info.offset + m_offset);
216,831✔
333
        node.update_ref_in_parent(child_info.key, ref);
216,831✔
334
    }
216,831✔
335
}
486,858✔
336

337
ref_type ClusterNodeInner::insert(RowKey row_key, const FieldValues& init_values, ClusterNode::State& state)
338
{
33,702,009✔
339
    return recurse<ref_type>(row_key, [this, &state, &init_values](ClusterNode* node, ChildInfo& child_info) {
33,702,009✔
340
        ref_type new_sibling_ref = node->insert(child_info.key, init_values, state);
33,624,684✔
341

342
        set_tree_size(get_tree_size() + 1);
33,624,684✔
343

344
        if (!new_sibling_ref) {
33,624,684✔
345
            return ref_type(0);
33,528,849✔
346
        }
33,528,849✔
347

348
        size_t new_ref_ndx = child_info.ndx + 1;
95,835✔
349

350
        int64_t split_key_value = state.split_key + child_info.offset;
95,835✔
351
        uint64_t sz = node_size();
95,835✔
352
        if (sz < cluster_node_size) {
105,696✔
353
            if (m_keys.is_attached()) {
92,529✔
354
                m_keys.insert(new_ref_ndx, split_key_value);
82,443✔
355
            }
82,443✔
356
            else {
10,086✔
357
                if (uint64_t(split_key_value) != sz << m_shift_factor) {
10,086✔
358
                    ensure_general_form();
288✔
359
                    m_keys.insert(new_ref_ndx, split_key_value);
288✔
360
                }
288✔
361
            }
10,086✔
362
            _insert_child_ref(new_ref_ndx, new_sibling_ref);
92,529✔
363
            return ref_type(0);
92,529✔
364
        }
92,529✔
365

366
        ClusterNodeInner child(m_alloc, m_tree_top);
2,147,496,814✔
367
        child.create(m_sub_tree_depth);
2,147,496,814✔
368
        if (new_ref_ndx == sz) {
2,147,496,814✔
369
            child.add(new_sibling_ref);
180✔
370
            state.split_key = split_key_value;
180✔
371
        }
180✔
372
        else {
2,147,496,724✔
373
            int64_t first_key_value = m_keys.get(new_ref_ndx);
2,147,496,724✔
374
            child.ensure_general_form();
2,147,496,724✔
375
            move(new_ref_ndx, &child, first_key_value);
2,147,496,724✔
376
            add(new_sibling_ref, split_key_value); // Throws
2,147,496,724✔
377
            state.split_key = first_key_value;
2,147,496,724✔
378
        }
2,147,496,724✔
379

380
        // Some objects has been moved out of this tree - find out how many
381
        size_t child_sub_tree_size = child.update_sub_tree_size();
2,147,496,814✔
382
        set_tree_size(get_tree_size() - child_sub_tree_size);
2,147,496,814✔
383

384
        return child.get_ref();
2,147,496,814✔
385
    });
95,835✔
386
}
33,702,009✔
387

388
bool ClusterNodeInner::try_get(RowKey key, ClusterNode::State& state) const noexcept
389
{
343,257,036✔
390
    ChildInfo child_info;
343,257,036✔
391
    if (!find_child(key, child_info)) {
343,257,036✔
392
        return false;
×
393
    }
×
394
    return const_cast<ClusterNodeInner*>(this)->recurse<bool>(child_info,
343,257,036✔
395
                                                              [&state](const ClusterNode* node, ChildInfo& info) {
343,257,036✔
396
                                                                  return node->try_get(info.key, state);
340,485,225✔
397
                                                              });
340,485,225✔
398
}
343,257,036✔
399

400
ObjKey ClusterNodeInner::get(size_t ndx, ClusterNode::State& state) const
401
{
12,008,382✔
402
    size_t sz = node_size();
12,008,382✔
403
    size_t child_ndx = 0;
12,008,382✔
404
    while (child_ndx < sz) {
75,359,331✔
405
        int64_t key_offset = m_keys.is_attached() ? m_keys.get(child_ndx) : (child_ndx << m_shift_factor);
75,351,288✔
406

407
        ref_type child_ref = _get_child_ref(child_ndx);
75,351,288✔
408
        char* child_header = m_alloc.translate(child_ref);
75,351,288✔
409
        bool child_is_leaf = !Array::get_is_inner_bptree_node_from_header(child_header);
75,351,288✔
410
        size_t sub_tree_size;
75,351,288✔
411
        if (child_is_leaf) {
75,351,288✔
412
            sub_tree_size = Cluster::node_size_from_header(m_alloc, child_header);
72,669,189✔
413
            if (ndx < sub_tree_size) {
72,669,189✔
414
                Cluster leaf(key_offset + m_offset, m_alloc, m_tree_top);
9,408,849✔
415
                leaf.init(MemRef(child_header, child_ref, m_alloc));
9,408,849✔
416
                REALM_ASSERT(sub_tree_size == leaf.get_tree_size());
9,408,849✔
417
                return leaf.get(ndx, state);
9,408,849✔
418
            }
9,408,849✔
419
        }
72,669,189✔
420
        else {
2,682,099✔
421
            ClusterNodeInner node(m_alloc, m_tree_top);
2,682,099✔
422
            node.init(MemRef(child_header, child_ref, m_alloc));
2,682,099✔
423
            node.set_offset(key_offset + m_offset);
2,682,099✔
424
            sub_tree_size = node.get_tree_size();
2,682,099✔
425
            if (ndx < sub_tree_size) {
2,688,183✔
426
                return node.get(ndx, state);
2,591,940✔
427
            }
2,591,940✔
428
        }
2,682,099✔
429
        child_ndx++;
63,350,499✔
430
        ndx -= sub_tree_size;
63,350,499✔
431
    }
63,350,499✔
432
    return {};
2,147,491,690✔
433
}
12,008,382✔
434

435
size_t ClusterNodeInner::get_ndx(RowKey key, size_t ndx) const noexcept
436
{
24,723✔
437
    ChildInfo child_info;
24,723✔
438
    if (!find_child(key, child_info)) {
24,723✔
439
        return realm::npos;
×
440
    }
×
441

442
    // First figure out how many objects there are in nodes before actual one
443
    // then descent in tree
444

445
    bool child_is_leaf = !Array::get_is_inner_bptree_node_from_header(child_info.mem.get_addr());
24,723✔
446
    if (child_is_leaf) {
24,723✔
447
        for (unsigned i = 0; i < child_info.ndx; i++) {
51,075✔
448
            ref_type ref = _get_child_ref(i);
26,352✔
449
            char* header = m_alloc.translate(ref);
26,352✔
450
            ndx += Cluster::node_size_from_header(m_alloc, header);
26,352✔
451
        }
26,352✔
452
        Cluster leaf(child_info.offset + m_offset, m_alloc, m_tree_top);
24,723✔
453
        leaf.init(child_info.mem);
24,723✔
454
        return leaf.get_ndx(child_info.key, ndx);
24,723✔
455
    }
24,723✔
456
    else {
×
457
        for (unsigned i = 0; i < child_info.ndx; i++) {
×
458
            char* header = m_alloc.translate(_get_child_ref(i));
×
459
            ndx += size_t(Array::get(header, s_sub_tree_size)) >> 1;
×
460
        }
×
461
        ClusterNodeInner node(m_alloc, m_tree_top);
×
462
        node.init(child_info.mem);
×
463
        node.set_offset(child_info.offset + m_offset);
×
464
        return node.get_ndx(child_info.key, ndx);
×
465
    }
×
466
}
24,723✔
467

468
void ClusterNodeInner::adjust_keys(int64_t adj)
469
{
30✔
470
    ensure_general_form();
30✔
471
    REALM_ASSERT(m_keys.get(0) == 0);
30✔
472
    m_keys.adjust(0, m_keys.size(), adj);
30✔
473

474
    // Now the first key offset value is 'adj' - it must be 0
475
    adjust_keys_first_child(adj);
30✔
476
}
30✔
477

478
void ClusterNodeInner::adjust_keys_first_child(int64_t adj)
479
{
12,060✔
480
    ref_type child_ref = _get_child_ref(0);
12,060✔
481
    char* child_header = m_alloc.translate(child_ref);
12,060✔
482
    auto mem = MemRef(child_header, child_ref, m_alloc);
12,060✔
483
    if (Array::get_is_inner_bptree_node_from_header(child_header)) {
12,060✔
484
        ClusterNodeInner node(m_alloc, m_tree_top);
30✔
485
        node.set_parent(this, s_first_node_index);
30✔
486
        node.init(mem);
30✔
487
        node.adjust_keys(adj);
30✔
488
    }
30✔
489
    else {
12,030✔
490
        Cluster node(0, m_alloc, m_tree_top);
12,030✔
491
        node.set_parent(this, s_first_node_index);
12,030✔
492
        node.init(mem);
12,030✔
493
        node.adjust_keys(adj);
12,030✔
494
    }
12,030✔
495
    m_keys.set(0, 0);
12,060✔
496
}
12,060✔
497

498
size_t ClusterNodeInner::erase(RowKey key, CascadeState& state)
499
{
8,434,560✔
500
    return recurse<size_t>(key, [this, &state](ClusterNode* erase_node, ChildInfo& child_info) {
8,434,560✔
501
        size_t erase_node_size = erase_node->erase(child_info.key, state);
8,434,101✔
502
        bool is_leaf = erase_node->is_leaf();
8,434,101✔
503
        set_tree_size(get_tree_size() - 1);
8,434,101✔
504

505
        if (erase_node_size == 0) {
8,434,101✔
506
            erase_node->destroy_deep();
13,356✔
507

508
            ensure_general_form();
13,356✔
509
            _erase_child_ref(child_info.ndx);
13,356✔
510
            m_keys.erase(child_info.ndx);
13,356✔
511
            if (child_info.ndx == 0 && m_keys.size() > 0) {
13,356✔
512
                auto first_offset = m_keys.get(0);
12,030✔
513
                // Adjust all key values in new first node
514
                // We have to make sure that the first key offset value
515
                // in all inner nodes is 0
516
                adjust_keys_first_child(first_offset);
12,030✔
517
            }
12,030✔
518
        }
13,356✔
519
        else if (erase_node_size < cluster_node_size / 2 && child_info.ndx < (node_size() - 1)) {
8,420,745✔
520
            // Candidate for merge. First calculate if the combined size of current and
521
            // next sibling is small enough.
522
            size_t sibling_ndx = child_info.ndx + 1;
3,306,111✔
523
            Cluster l2(child_info.offset, m_alloc, m_tree_top);
3,306,111✔
524
            ClusterNodeInner n2(m_alloc, m_tree_top);
3,306,111✔
525
            ClusterNode* sibling_node = is_leaf ? static_cast<ClusterNode*>(&l2) : static_cast<ClusterNode*>(&n2);
3,306,111✔
526
            sibling_node->set_parent(this, sibling_ndx + s_first_node_index);
3,306,111✔
527
            sibling_node->init_from_parent();
3,306,111✔
528

529
            size_t combined_size = sibling_node->node_size() + erase_node_size;
3,306,111✔
530

531
            if (combined_size < cluster_node_size * 3 / 4) {
3,306,111✔
532
                // Calculate value that must be subtracted from the moved keys
533
                // (will be negative as the sibling has bigger keys)
534
                int64_t key_adj = m_keys.is_attached() ? (m_keys.get(child_info.ndx) - m_keys.get(sibling_ndx))
7,749✔
535
                                                       : 0 - (1 << m_shift_factor);
7,749✔
536
                // And then move all elements into current node
537
                sibling_node->ensure_general_form();
7,749✔
538
                erase_node->ensure_general_form();
7,749✔
539
                sibling_node->move(0, erase_node, key_adj);
7,749✔
540

541
                if (!erase_node->is_leaf()) {
7,749✔
542
                    static_cast<ClusterNodeInner*>(erase_node)->update_sub_tree_size();
45✔
543
                }
45✔
544

545
                // Destroy sibling
546
                sibling_node->destroy_deep();
7,749✔
547

548
                ensure_general_form();
7,749✔
549
                _erase_child_ref(sibling_ndx);
7,749✔
550
                m_keys.erase(sibling_ndx);
7,749✔
551
            }
7,749✔
552
        }
3,306,111✔
553

554
        return node_size();
8,434,101✔
555
    });
8,434,101✔
556
}
8,434,560✔
557

558
void ClusterNodeInner::nullify_incoming_links(RowKey key, CascadeState& state)
559
{
8,364,102✔
560
    recurse<void>(key, [&state](ClusterNode* node, ChildInfo& child_info) {
8,364,102✔
561
        node->nullify_incoming_links(child_info.key, state);
8,363,607✔
562
    });
8,363,607✔
563
}
8,364,102✔
564

565
void ClusterNodeInner::ensure_general_form()
566
{
24,330✔
567
    if (!m_keys.is_attached()) {
24,330✔
568
        size_t current_size = node_size();
3,291✔
569
        m_keys.create(current_size, (current_size - 1) << m_shift_factor);
3,291✔
570
        m_keys.update_parent();
3,291✔
571
        for (size_t i = 0; i < current_size; i++) {
10,875✔
572
            m_keys.set(i, i << m_shift_factor);
7,584✔
573
        }
7,584✔
574
    }
3,291✔
575
}
24,330✔
576

577
void ClusterNodeInner::insert_column(ColKey col)
578
{
162✔
579
    size_t sz = node_size();
162✔
580
    for (size_t i = 0; i < sz; i++) {
24,252✔
581
        std::shared_ptr<ClusterNode> node = m_tree_top.get_node(this, i + s_first_node_index);
24,090✔
582
        node->insert_column(col);
24,090✔
583
    }
24,090✔
584
}
162✔
585

586
void ClusterNodeInner::remove_column(ColKey col)
587
{
6✔
588
    size_t sz = node_size();
6✔
589
    for (size_t i = 0; i < sz; i++) {
18✔
590
        std::shared_ptr<ClusterNode> node = m_tree_top.get_node(this, i + s_first_node_index);
12✔
591
        node->remove_column(col);
12✔
592
    }
12✔
593
}
6✔
594

595
size_t ClusterNodeInner::nb_columns() const
596
{
×
597
    ref_type ref = _get_child_ref(0);
×
598
    char* header = m_alloc.translate(ref);
×
599
    bool child_is_leaf = !Array::get_is_inner_bptree_node_from_header(header);
×
600
    MemRef mem(header, ref, m_alloc);
×
601
    if (child_is_leaf) {
×
602
        Cluster leaf(0, m_alloc, m_tree_top);
×
603
        leaf.init(mem);
×
604
        return leaf.nb_columns();
×
605
    }
×
606
    else {
×
607
        ClusterNodeInner node(m_alloc, m_tree_top);
×
608
        node.init(mem);
×
609
        return node.nb_columns();
×
610
    }
×
611
}
×
612

613
void ClusterNodeInner::add(ref_type ref, int64_t key_value)
614
{
6,855✔
615
    if (m_keys.is_attached()) {
6,855✔
616
        m_keys.add(key_value);
33✔
617
    }
33✔
618
    else {
6,822✔
619
        if (uint64_t(key_value) != (uint64_t(node_size()) << m_shift_factor)) {
6,822✔
620
            ensure_general_form();
2,784✔
621
            m_keys.add(key_value);
2,784✔
622
        }
2,784✔
623
    }
6,822✔
624
    Array::add(from_ref(ref));
6,855✔
625
}
6,855✔
626

627
// Find leaf that contains the object identified by key. If this does not exist return the
628
// leaf that contains the next object
629
bool ClusterNodeInner::get_leaf(RowKey key, ClusterNode::IteratorState& state) const noexcept
630
{
2,691,564✔
631
    size_t child_ndx;
2,691,564✔
632
    if (m_keys.is_attached()) {
2,691,564✔
633
        child_ndx = m_keys.upper_bound(key.value);
2,652,696✔
634
        if (child_ndx > 0)
2,652,696✔
635
            child_ndx--;
2,659,671✔
636
    }
2,652,696✔
637
    else {
38,868✔
638
        REALM_ASSERT_DEBUG(node_size() > 0);
38,868✔
639
        size_t max_ndx = node_size() - 1;
38,868✔
640
        child_ndx = std::min(size_t(key.value >> m_shift_factor), max_ndx);
38,868✔
641
    }
38,868✔
642

643
    size_t sz = node_size();
2,691,564✔
644
    while (child_ndx < sz) {
2,740,017✔
645
        uint64_t key_offset = m_keys.is_attached() ? m_keys.get(child_ndx) : (child_ndx << m_shift_factor);
2,734,893✔
646
        RowKey new_key(key_offset < key.value ? key.value - key_offset : 0);
2,734,893✔
647
        state.m_key_offset += key_offset;
2,734,893✔
648

649
        ref_type child_ref = _get_child_ref(child_ndx);
2,734,893✔
650
        char* child_header = m_alloc.translate(child_ref);
2,734,893✔
651
        bool child_is_leaf = !Array::get_is_inner_bptree_node_from_header(child_header);
2,734,893✔
652
        if (child_is_leaf) {
2,734,893✔
653
            state.m_current_leaf.init(MemRef(child_header, child_ref, m_alloc));
1,536,912✔
654
            state.m_current_leaf.set_offset(state.m_key_offset);
1,536,912✔
655
            state.m_current_index = state.m_current_leaf.lower_bound_key(new_key);
1,536,912✔
656
            if (state.m_current_index < state.m_current_leaf.node_size())
1,536,912✔
657
                return true;
1,483,437✔
658
        }
1,536,912✔
659
        else {
1,197,981✔
660
            ClusterNodeInner node(m_alloc, m_tree_top);
1,197,981✔
661
            node.init(MemRef(child_header, child_ref, m_alloc));
1,197,981✔
662
            if (node.get_leaf(new_key, state))
1,197,981✔
663
                return true;
1,203,003✔
664
        }
1,197,981✔
665
        state.m_key_offset -= key_offset;
48,453✔
666
        child_ndx++;
48,453✔
667
    }
48,453✔
668
    return false;
5,124✔
669
}
2,691,564✔
670

671
// LCOV_EXCL_START
672
void ClusterNodeInner::dump_objects(int64_t key_offset, std::string lead) const
673
{
×
674
    std::cout << lead << "node" << std::endl;
×
675
    if (!m_keys.is_attached()) {
×
676
        std::cout << lead << "compact form" << std::endl;
×
677
    }
×
678
    size_t sz = node_size();
×
679
    for (unsigned i = 0; i < sz; i++) {
×
680
        int64_t key_value;
×
681
        if (m_keys.is_attached()) {
×
682
            key_value = m_keys.get(i) + key_offset;
×
683
        }
×
684
        else {
×
685
            key_value = (i << m_shift_factor) + key_offset;
×
686
        }
×
687
        std::cout << lead << std::hex << "split: " << key_value << std::dec << std::endl;
×
688
        m_tree_top.get_node(const_cast<ClusterNodeInner*>(this), i + s_first_node_index)
×
689
            ->dump_objects(key_value, lead + "   ");
×
690
    }
×
691
}
×
692
// LCOV_EXCL_STOP
693
void ClusterNodeInner::move(size_t ndx, ClusterNode* new_node, int64_t key_adj)
694
{
78✔
695
    auto new_cluster_node_inner = static_cast<ClusterNodeInner*>(new_node);
78✔
696
    for (size_t i = ndx; i < node_size(); i++) {
6,726✔
697
        new_cluster_node_inner->Array::add(_get_child_ref(i));
6,648✔
698
    }
6,648✔
699
    for (size_t i = ndx; i < m_keys.size(); i++) {
6,726✔
700
        new_cluster_node_inner->m_keys.add(m_keys.get(i) - key_adj);
6,648✔
701
    }
6,648✔
702
    truncate(ndx + s_first_node_index);
78✔
703
    if (m_keys.is_attached()) {
78✔
704
        m_keys.truncate(ndx);
78✔
705
    }
78✔
706
}
78✔
707

708
size_t ClusterNodeInner::update_sub_tree_size()
709
{
3,579✔
710
    size_t sub_tree_size = 0;
3,579✔
711
    auto sz = node_size();
3,579✔
712

713
    for (unsigned i = 0; i < sz; i++) {
21,000✔
714
        ref_type ref = _get_child_ref(i);
17,421✔
715
        char* header = m_alloc.translate(ref);
17,421✔
716
        bool child_is_leaf = !Array::get_is_inner_bptree_node_from_header(header);
17,421✔
717
        if (child_is_leaf) {
17,421✔
718
            sub_tree_size += Cluster::node_size_from_header(m_alloc, header);
17,289✔
719
        }
17,289✔
720
        else {
132✔
721
            sub_tree_size += size_t(Array::get(header, s_sub_tree_size)) >> 1;
132✔
722
        }
132✔
723
    }
17,421✔
724
    set_tree_size(sub_tree_size);
3,579✔
725
    return sub_tree_size;
3,579✔
726
}
3,579✔
727

728
bool ClusterNodeInner::traverse(ClusterTree::TraverseFunction func, int64_t key_offset) const
729
{
1,698,153✔
730
    auto sz = node_size();
1,698,153✔
731

732
    for (unsigned i = 0; i < sz; i++) {
8,485,146✔
733
        ref_type ref = _get_child_ref(i);
6,816,126✔
734
        char* header = m_alloc.translate(ref);
6,816,126✔
735
        bool child_is_leaf = !Array::get_is_inner_bptree_node_from_header(header);
6,816,126✔
736
        MemRef mem(header, ref, m_alloc);
6,816,126✔
737
        int64_t offs = (m_keys.is_attached() ? m_keys.get(i) : i << m_shift_factor) + key_offset;
6,816,126✔
738
        if (child_is_leaf) {
6,816,126✔
739
            Cluster leaf(offs, m_alloc, m_tree_top);
6,329,493✔
740
            leaf.init(mem);
6,329,493✔
741
            if (func(&leaf) == IteratorControl::Stop) {
6,329,493✔
742
                return true;
29,133✔
743
            }
29,133✔
744
        }
6,329,493✔
745
        else {
486,633✔
746
            ClusterNodeInner node(m_alloc, m_tree_top);
486,633✔
747
            node.init(mem);
486,633✔
748
            if (node.traverse(func, offs)) {
486,633✔
749
                return true;
×
750
            }
×
751
        }
486,633✔
752
    }
6,816,126✔
753
    return false;
1,669,020✔
754
}
1,698,153✔
755

756
void ClusterNodeInner::update(ClusterTree::UpdateFunction func, int64_t key_offset)
757
{
36✔
758
    auto sz = node_size();
36✔
759

760
    for (unsigned i = 0; i < sz; i++) {
468✔
761
        ref_type ref = _get_child_ref(i);
432✔
762
        char* header = m_alloc.translate(ref);
432✔
763
        bool child_is_leaf = !Array::get_is_inner_bptree_node_from_header(header);
432✔
764
        MemRef mem(header, ref, m_alloc);
432✔
765
        int64_t offs = (m_keys.is_attached() ? m_keys.get(i) : i << m_shift_factor) + key_offset;
432✔
766
        if (child_is_leaf) {
432✔
767
            Cluster leaf(offs, m_alloc, m_tree_top);
432✔
768
            leaf.init(mem);
432✔
769
            leaf.set_parent(this, i + s_first_node_index);
432✔
770
            func(&leaf);
432✔
771
        }
432✔
772
        else {
×
773
            ClusterNodeInner node(m_alloc, m_tree_top);
×
774
            node.init(mem);
×
775
            node.set_parent(this, i + s_first_node_index);
×
776
            node.update(func, offs);
×
777
        }
×
778
    }
432✔
779
}
36✔
780

781
int64_t ClusterNodeInner::get_last_key_value() const
782
{
×
783
    auto last_ndx = node_size() - 1;
×
784

785
    ref_type ref = _get_child_ref(last_ndx);
×
786
    char* header = m_alloc.translate(ref);
×
787
    bool child_is_leaf = !Array::get_is_inner_bptree_node_from_header(header);
×
788
    MemRef mem(header, ref, m_alloc);
×
789
    int64_t offset = m_keys.is_attached() ? m_keys.get(last_ndx) : last_ndx << m_shift_factor;
×
790
    if (child_is_leaf) {
×
791
        Cluster leaf(offset, m_alloc, m_tree_top);
×
792
        leaf.init(mem);
×
793
        return offset + leaf.get_last_key_value();
×
794
    }
×
795
    else {
×
796
        ClusterNodeInner node(m_alloc, m_tree_top);
×
797
        node.init(mem);
×
798
        return offset + node.get_last_key_value();
×
799
    }
×
800
}
×
801

802
ClusterTree::ClusterTree(Table* owner, Allocator& alloc, size_t top_position_for_cluster_tree)
803
    : m_alloc(alloc)
24,015✔
804
    , m_owner(owner)
24,015✔
805
    , m_top_position_for_cluster_tree(top_position_for_cluster_tree)
24,015✔
806
{
55,101✔
807
}
55,101✔
808

809
ClusterTree::~ClusterTree() {}
33,525✔
810

811
std::unique_ptr<ClusterNode> ClusterTree::create_root_from_parent(ArrayParent* parent, size_t ndx_in_parent)
812
{
2,420,490✔
813
    ref_type ref = parent->get_child_ref(ndx_in_parent);
2,420,490✔
814
    if (!ref)
2,420,490✔
815
        return nullptr;
×
816

817
    MemRef mem{m_alloc.translate(ref), ref, m_alloc};
2,420,490✔
818
    const char* header = mem.get_addr();
2,420,490✔
819
    bool is_leaf = !Array::get_is_inner_bptree_node_from_header(header);
2,420,490✔
820

821
    bool can_reuse_root_accessor = m_root && m_root->is_leaf() == is_leaf;
2,420,490✔
822
    if (can_reuse_root_accessor) {
2,420,490✔
823
        m_root->init(mem);
2,286,828✔
824
        return std::move(m_root); // Same root will be reinstalled.
2,286,828✔
825
    }
2,286,828✔
826

827
    // Not reusing root note, allocating a new one.
828
    std::unique_ptr<ClusterNode> new_root;
133,662✔
829
    if (is_leaf) {
133,662✔
830
        new_root = std::make_unique<Cluster>(0, m_alloc, *this);
91,473✔
831
    }
91,473✔
832
    else {
42,189✔
833
        new_root = std::make_unique<ClusterNodeInner>(m_alloc, *this);
42,189✔
834
    }
42,189✔
835
    new_root->init(mem);
133,662✔
836
    new_root->set_parent(parent, ndx_in_parent);
133,662✔
837

838
    return new_root;
133,662✔
839
}
2,420,490✔
840

841
std::unique_ptr<ClusterNode> ClusterTree::get_node(ArrayParent* parent, size_t ndx_in_parent) const
842
{
24,342✔
843
    ref_type ref = parent->get_child_ref(ndx_in_parent);
24,342✔
844

845
    std::unique_ptr<ClusterNode> node;
24,342✔
846

847
    char* child_header = static_cast<char*>(m_alloc.translate(ref));
24,342✔
848
    bool child_is_leaf = !Array::get_is_inner_bptree_node_from_header(child_header);
24,342✔
849
    if (child_is_leaf) {
24,342✔
850
        node = std::make_unique<Cluster>(0, m_alloc, *this);
24,228✔
851
    }
24,228✔
852
    else {
114✔
853
        node = std::make_unique<ClusterNodeInner>(m_alloc, *this);
114✔
854
    }
114✔
855
    node->init(MemRef(child_header, ref, m_alloc));
24,342✔
856
    node->set_parent(parent, ndx_in_parent);
24,342✔
857

858
    return node;
24,342✔
859
}
24,342✔
860

861
void ClusterTree::clear(CascadeState& state)
862
{
18,168✔
863
    m_owner->clear_indexes();
18,168✔
864

865
    if (state.m_group) {
18,168✔
866
        remove_all_links(state); // This will also delete objects loosing their last strong link
17,016✔
867
    }
17,016✔
868

869
    // We no longer have "clear table" instruction, so we have to report the removal of each
870
    // object individually
871
    if (Replication* repl = m_owner->get_repl()) {
18,168✔
872
        // Go through all clusters
873
        traverse([repl, this](const Cluster* cluster) {
15,861✔
874
            auto sz = cluster->node_size();
15,861✔
875
            for (size_t i = 0; i < sz; i++) {
117,159✔
876
                repl->remove_object(m_owner, cluster->get_real_key(i));
101,298✔
877
            }
101,298✔
878
            return IteratorControl::AdvanceToNext;
15,861✔
879
        });
15,861✔
880
    }
15,507✔
881

882
    m_root->destroy_deep();
18,168✔
883

884
    auto leaf = std::make_unique<Cluster>(0, m_root->get_alloc(), *this);
18,168✔
885
    leaf->create();
18,168✔
886
    replace_root(std::move(leaf));
18,168✔
887

888
    bump_content_version();
18,168✔
889
    bump_storage_version();
18,168✔
890

891
    m_size = 0;
18,168✔
892
}
18,168✔
893

894
void ClusterTree::enumerate_string_column(ColKey col_key)
895
{
690✔
896
    Allocator& alloc = get_alloc();
690✔
897

898
    ArrayString keys(alloc);
690✔
899
    ArrayString leaf(alloc);
690✔
900
    keys.create();
690✔
901

902
    auto collect_strings = [col_key, &leaf, &keys](const Cluster* cluster) {
1,086✔
903
        cluster->init_leaf(col_key, &leaf);
1,086✔
904
        size_t sz = leaf.size();
1,086✔
905
        size_t key_size = keys.size();
1,086✔
906
        for (size_t i = 0; i < sz; i++) {
118,542✔
907
            auto v = leaf.get(i);
117,456✔
908
            size_t pos = keys.lower_bound(v);
117,456✔
909
            if (pos == key_size || keys.get(pos) != v) {
117,456✔
910
                keys.insert(pos, v); // Throws
576✔
911
                key_size++;
576✔
912
            }
576✔
913
        }
117,456✔
914

915
        return IteratorControl::AdvanceToNext;
1,086✔
916
    };
1,086✔
917

918
    auto upgrade = [col_key, &keys](Cluster* cluster) {
1,086✔
919
        cluster->upgrade_string_to_enum(col_key, keys);
1,086✔
920
    };
1,086✔
921

922
    // Populate 'keys' array
923
    traverse(collect_strings);
690✔
924

925
    // Store key strings in spec
926
    size_t spec_ndx = m_owner->colkey2spec_ndx(col_key);
690✔
927
    const_cast<Spec*>(&m_owner->m_spec)->upgrade_string_to_enum(spec_ndx, keys.get_ref());
690✔
928

929
    // Replace column in all clusters
930
    update(upgrade);
690✔
931
}
690✔
932

933
void ClusterTree::replace_root(std::unique_ptr<ClusterNode> new_root)
934
{
21,729✔
935
    if (new_root != m_root) {
21,729✔
936
        // Maintain parent.
937
        new_root->set_parent(m_root->get_parent(), m_root->get_ndx_in_parent());
21,729✔
938
        new_root->update_parent(); // Throws
21,729✔
939
        m_root = std::move(new_root);
21,729✔
940
    }
21,729✔
941
}
21,729✔
942

943
bool ClusterTree::init_from_parent()
944
{
2,418,579✔
945
    m_root = get_root_from_parent();
2,418,579✔
946
    if (m_root) {
2,421,606✔
947
        m_size = m_root->get_tree_size();
2,421,606✔
948
        return true;
2,421,606✔
949
    }
2,421,606✔
950
    m_size = 0;
4,294,967,294✔
951
    return false;
4,294,967,294✔
952
}
2,418,579✔
953

954
void ClusterTree::update_from_parent() noexcept
955
{
1,036,143✔
956
    m_root->update_from_parent();
1,036,143✔
957
    m_size = m_root->get_tree_size();
1,036,143✔
958
}
1,036,143✔
959

960
void ClusterTree::insert_fast(ObjKey k, const FieldValues& init_values, ClusterNode::State& state)
961
{
25,028,079✔
962
    ref_type new_sibling_ref = m_root->insert(ClusterNode::RowKey(k), init_values, state);
25,028,079✔
963
    if (REALM_UNLIKELY(new_sibling_ref)) {
25,028,079✔
964
        auto new_root = std::make_unique<ClusterNodeInner>(m_root->get_alloc(), *this);
3,321✔
965
        new_root->create(m_root->get_sub_tree_depth() + 1);
3,321✔
966

967
        new_root->add(m_root->get_ref());                // Throws
3,321✔
968
        new_root->add(new_sibling_ref, state.split_key); // Throws
3,321✔
969
        new_root->update_sub_tree_size();
3,321✔
970

971
        replace_root(std::move(new_root));
3,321✔
972
    }
3,321✔
973
    m_size++;
25,028,079✔
974
}
25,028,079✔
975

976
Obj ClusterTree::insert(ObjKey k, const FieldValues& init_values)
977
{
25,018,248✔
978
    ClusterNode::State state;
25,018,248✔
979

980
    insert_fast(k, init_values, state);
25,018,248✔
981
    m_owner->update_indexes(k, init_values);
25,018,248✔
982

983
    bump_content_version();
25,018,248✔
984
    bump_storage_version();
25,018,248✔
985

986
    // Replicate setting of values
987
    if (Replication* repl = m_owner->get_repl()) {
25,018,248✔
988
        auto pk_col = m_owner->get_primary_key_column();
8,947,551✔
989
        for (const auto& v : init_values) {
8,947,551✔
990
            if (v.col_key != pk_col)
465,294✔
991
                repl->set(m_owner, v.col_key, k, v.value, v.is_default ? _impl::instr_SetDefault : _impl::instr_Set);
89,526✔
992
        }
465,294✔
993
    }
8,947,551✔
994

995
    return Obj(get_table_ref(), state.mem, k, state.index);
25,018,248✔
996
}
25,018,248✔
997

998
bool ClusterTree::is_valid(ObjKey k) const noexcept
999
{
85,794,837✔
1000
    if (m_size == 0)
85,794,837✔
1001
        return false;
207,954✔
1002

1003
    ClusterNode::State state;
85,586,883✔
1004
    return m_root->try_get(ClusterNode::RowKey(k), state);
85,586,883✔
1005
}
85,794,837✔
1006

1007
ClusterNode::State ClusterTree::try_get(ObjKey k) const noexcept
1008
{
29,042,109✔
1009
    ClusterNode::State state;
29,042,109✔
1010
    if (!(k && m_root->try_get(ClusterNode::RowKey(k), state)))
29,063,286✔
1011
        state.index = realm::npos;
53,529✔
1012
    return state;
29,042,109✔
1013
}
29,042,109✔
1014

1015
ClusterNode::State ClusterTree::get(size_t ndx, ObjKey& k) const
1016
{
12,198,042✔
1017
    if (ndx >= m_size) {
12,198,042✔
1018
        throw Exception(ErrorCodes::InvalidatedObject, "Object was deleted");
×
1019
    }
×
1020
    ClusterNode::State state;
12,198,042✔
1021
    k = m_root->get(ndx, state);
12,198,042✔
1022
    return state;
12,198,042✔
1023
}
12,198,042✔
1024

1025
size_t ClusterTree::get_ndx(ObjKey k) const noexcept
1026
{
36,744✔
1027
    return m_root->get_ndx(ClusterNode::RowKey(k), 0);
36,744✔
1028
}
36,744✔
1029

1030
void ClusterTree::erase(ObjKey k, CascadeState& state)
1031
{
5,323,098✔
1032
    if (!k.is_unresolved()) {
5,323,098✔
1033
        if (auto table = get_owning_table()) {
5,283,465✔
1034
            if (Replication* repl = table->get_repl()) {
5,283,447✔
1035
                repl->remove_object(table, k);
4,834,398✔
1036
            }
4,834,398✔
1037
        }
5,283,447✔
1038
    }
5,283,465✔
1039
    m_owner->free_local_id_after_hash_collision(k);
5,323,098✔
1040
    m_owner->erase_from_search_indexes(k);
5,323,098✔
1041

1042
    size_t root_size = m_root->erase(ClusterNode::RowKey(k), state);
5,323,098✔
1043

1044
    bump_content_version();
5,323,098✔
1045
    bump_storage_version();
5,323,098✔
1046
    m_size--;
5,323,098✔
1047
    while (!m_root->is_leaf() && root_size == 1) {
5,323,338✔
1048
        ClusterNodeInner* node = static_cast<ClusterNodeInner*>(m_root.get());
240✔
1049

1050
        REALM_ASSERT(node->get_first_key_value() == 0);
240✔
1051
        auto new_root = node->return_and_clear_first_child();
240✔
1052
        node->destroy_deep();
240✔
1053

1054
        replace_root(std::move(new_root));
240✔
1055
        root_size = m_root->node_size();
240✔
1056
    }
240✔
1057
}
5,323,098✔
1058

1059
bool ClusterTree::get_leaf(ObjKey key, ClusterNode::IteratorState& state) const noexcept
1060
{
2,174,280✔
1061
    state.clear();
2,174,280✔
1062
    ClusterNode::RowKey row_key(key);
2,174,280✔
1063
    if (m_root->is_leaf()) {
2,174,280✔
1064
        Cluster* node = static_cast<Cluster*>(m_root.get());
688,662✔
1065
        REALM_ASSERT_DEBUG(node->get_offset() == 0);
688,662✔
1066
        state.m_key_offset = 0;
688,662✔
1067
        state.m_current_leaf.init(node->get_mem());
688,662✔
1068
        state.m_current_leaf.set_offset(state.m_key_offset);
688,662✔
1069
        state.m_current_index = node->lower_bound_key(row_key);
688,662✔
1070
        return state.m_current_index < state.m_current_leaf.node_size();
688,662✔
1071
    }
688,662✔
1072
    else {
1,485,618✔
1073
        ClusterNodeInner* node = static_cast<ClusterNodeInner*>(m_root.get());
1,485,618✔
1074
        return node->get_leaf(row_key, state);
1,485,618✔
1075
    }
1,485,618✔
1076
}
2,174,280✔
1077

1078
bool ClusterTree::traverse(TraverseFunction func) const
1079
{
2,301,753✔
1080
    if (m_root->is_leaf()) {
2,301,753✔
1081
        return func(static_cast<Cluster*>(m_root.get())) == IteratorControl::Stop;
745,029✔
1082
    }
745,029✔
1083
    else {
1,556,724✔
1084
        return static_cast<ClusterNodeInner*>(m_root.get())->traverse(func, 0);
1,556,724✔
1085
    }
1,556,724✔
1086
}
2,301,753✔
1087

1088
void ClusterTree::update(UpdateFunction func)
1089
{
972✔
1090
    if (m_root->is_leaf()) {
972✔
1091
        func(static_cast<Cluster*>(m_root.get()));
936✔
1092
    }
936✔
1093
    else {
36✔
1094
        static_cast<ClusterNodeInner*>(m_root.get())->update(func, 0);
36✔
1095
    }
36✔
1096
}
972✔
1097

1098
void ClusterTree::set_spec(ArrayPayload& arr, ColKey::Idx col_ndx) const
1099
{
3,925,548✔
1100
    // Check for owner. This function may be called in context of DictionaryClusterTree
1101
    // in which case m_owner is null (and spec never needed).
1102
    if (m_owner) {
3,925,644✔
1103
        auto spec_ndx = m_owner->leaf_ndx2spec_ndx(col_ndx);
3,925,644✔
1104
        arr.set_spec(&m_owner->m_spec, spec_ndx);
3,925,644✔
1105
    }
3,925,644✔
1106
}
3,925,548✔
1107

1108
TableRef ClusterTree::get_table_ref() const
1109
{
245,402,943✔
1110
    REALM_ASSERT(m_owner != nullptr);
245,402,943✔
1111
    // as safe as storing the TableRef locally in the ClusterTree,
1112
    // because the cluster tree and the table is basically one object :-O
1113
    return m_owner->m_own_ref;
245,402,943✔
1114
}
245,402,943✔
1115

1116
std::unique_ptr<ClusterNode> ClusterTree::get_root_from_parent()
1117
{
2,419,095✔
1118
    return create_root_from_parent(&m_owner->m_top, m_top_position_for_cluster_tree);
2,419,095✔
1119
}
2,419,095✔
1120

1121
void ClusterTree::verify() const
1122
{
256,290✔
1123
#ifdef REALM_DEBUG
256,290✔
1124
    traverse([](const Cluster* cluster) {
376,341✔
1125
        cluster->verify();
376,341✔
1126
        return IteratorControl::AdvanceToNext;
376,341✔
1127
    });
376,341✔
1128
#endif
256,290✔
1129
}
256,290✔
1130

1131
void ClusterTree::nullify_incoming_links(ObjKey obj_key, CascadeState& state)
1132
{
5,192,445✔
1133
    REALM_ASSERT(state.m_group);
5,192,445✔
1134
    m_root->nullify_incoming_links(ClusterNode::RowKey(obj_key), state);
5,192,445✔
1135
}
5,192,445✔
1136

1137
bool ClusterTree::is_string_enum_type(ColKey::Idx col_ndx) const
1138
{
73,659✔
1139
    size_t spec_ndx = m_owner->leaf_ndx2spec_ndx(col_ndx);
73,659✔
1140
    return m_owner->m_spec.is_string_enum_type(spec_ndx);
73,659✔
1141
}
73,659✔
1142

1143
void ClusterTree::remove_all_links(CascadeState& state)
1144
{
17,016✔
1145
    Allocator& alloc = get_alloc();
17,016✔
1146
    auto origin_table = get_owning_table();
17,016✔
1147
    // This function will add objects that should be deleted to 'state'
1148
    auto func = [&](const Cluster* cluster) {
17,394✔
1149
        auto remove_link_from_column = [&](ColKey col_key) {
140,466✔
1150
            // Prevent making changes to table that is going to be removed anyway
1151
            // Furthermore it is a prerequisite for using 'traverse' that the tree
1152
            // is not modified
1153
            if (origin_table->links_to_self(col_key)) {
140,466✔
1154
                return IteratorControl::AdvanceToNext;
246✔
1155
            }
246✔
1156
            auto col_type = col_key.get_type();
140,220✔
1157
            if (col_key.is_collection()) {
140,220✔
1158
                ArrayInteger values(alloc);
5,424✔
1159
                cluster->init_leaf(col_key, &values);
5,424✔
1160
                size_t sz = values.size();
5,424✔
1161

1162
                for (size_t i = 0; i < sz; i++) {
18,600✔
1163
                    if (ref_type ref = values.get_as_ref(i)) {
13,176✔
1164
                        ObjKey origin_key = cluster->get_real_key(i);
390✔
1165
                        if (col_key.is_list() || col_key.is_set()) {
390✔
1166
                            if (col_type == col_type_Link) {
318✔
1167
                                BPlusTree<ObjKey> links(alloc);
258✔
1168
                                links.init_from_ref(ref);
258✔
1169
                                if (links.size() > 0) {
258✔
1170
                                    cluster->do_remove_backlinks(origin_key, col_key, links.get_all(), state);
246✔
1171
                                }
246✔
1172
                            }
258✔
1173
                            else if (col_type == col_type_TypedLink) {
60✔
1174
                                BPlusTree<ObjLink> links(alloc);
×
1175
                                links.init_from_ref(ref);
×
1176
                                if (links.size() > 0) {
×
1177
                                    cluster->do_remove_backlinks(origin_key, col_key, links.get_all(), state);
×
1178
                                }
×
1179
                            }
×
1180
                            else if (col_type == col_type_Mixed) {
60✔
1181
                                BPlusTree<Mixed> mix_arr(alloc);
60✔
1182
                                mix_arr.init_from_ref(ref);
60✔
1183
                                auto sz = mix_arr.size();
60✔
1184
                                std::vector<ObjLink> links;
60✔
1185
                                for (size_t j = 0; j < sz; j++) {
180✔
1186
                                    auto mix = mix_arr.get(j);
120✔
1187
                                    if (mix.is_type(type_TypedLink)) {
120✔
1188
                                        links.push_back(mix.get<ObjLink>());
120✔
1189
                                    }
120✔
1190
                                }
120✔
1191
                                if (links.size())
60✔
1192
                                    cluster->do_remove_backlinks(origin_key, col_key, links, state);
60✔
1193
                            }
60✔
1194
                        }
318✔
1195
                        else if (col_key.is_dictionary()) {
72✔
1196
                            std::vector<ObjLink> links;
72✔
1197

1198
                            Array top(alloc);
72✔
1199
                            top.set_parent(&values, i);
72✔
1200
                            top.init_from_parent();
72✔
1201
                            BPlusTree<Mixed> values(alloc);
72✔
1202
                            values.set_parent(&top, 1);
72✔
1203
                            values.init_from_parent();
72✔
1204

1205
                            // Iterate through values and insert all link values
1206
                            values.for_all([&](Mixed val) {
108✔
1207
                                if (val.is_type(type_TypedLink)) {
108✔
1208
                                    links.push_back(val.get<ObjLink>());
108✔
1209
                                }
108✔
1210
                            });
108✔
1211

1212
                            if (links.size() > 0) {
72✔
1213
                                cluster->do_remove_backlinks(origin_key, col_key, links, state);
72✔
1214
                            }
72✔
1215
                        }
72✔
1216
                    }
390✔
1217
                }
13,176✔
1218
            }
5,424✔
1219
            else {
134,796✔
1220
                if (col_type == col_type_Link) {
134,796✔
1221
                    ArrayKey values(alloc);
1,506✔
1222
                    cluster->init_leaf(col_key, &values);
1,506✔
1223
                    size_t sz = values.size();
1,506✔
1224
                    for (size_t i = 0; i < sz; i++) {
4,638✔
1225
                        if (ObjKey key = values.get(i)) {
3,132✔
1226
                            cluster->do_remove_backlinks(cluster->get_real_key(i), col_key, std::vector<ObjKey>{key},
522✔
1227
                                                         state);
522✔
1228
                        }
522✔
1229
                    }
3,132✔
1230
                }
1,506✔
1231
                else if (col_type == col_type_TypedLink) {
133,290✔
1232
                    ArrayTypedLink values(alloc);
×
1233
                    cluster->init_leaf(col_key, &values);
×
1234
                    size_t sz = values.size();
×
1235
                    for (size_t i = 0; i < sz; i++) {
×
1236
                        if (ObjLink link = values.get(i)) {
×
1237
                            cluster->do_remove_backlinks(cluster->get_real_key(i), col_key,
×
1238
                                                         std::vector<ObjLink>{link}, state);
×
1239
                        }
×
1240
                    }
×
1241
                }
×
1242
                else if (col_type == col_type_Mixed) {
133,290✔
1243
                    ArrayMixed values(alloc);
108✔
1244
                    cluster->init_leaf(col_key, &values);
108✔
1245
                    size_t sz = values.size();
108✔
1246
                    for (size_t i = 0; i < sz; i++) {
648✔
1247
                        Mixed mix = values.get(i);
540✔
1248
                        if (mix.is_type(type_TypedLink)) {
540✔
1249
                            cluster->do_remove_backlinks(cluster->get_real_key(i), col_key,
12✔
1250
                                                         std::vector<ObjLink>{mix.get<ObjLink>()}, state);
12✔
1251
                        }
12✔
1252
                    }
540✔
1253
                }
108✔
1254
                else if (col_type == col_type_BackLink) {
133,182✔
1255
                    ArrayBacklink values(alloc);
279✔
1256
                    cluster->init_leaf(col_key, &values);
279✔
1257
                    values.set_parent(const_cast<Cluster*>(cluster),
279✔
1258
                                      col_key.get_index().val + Cluster::s_first_col_index);
279✔
1259
                    size_t sz = values.size();
279✔
1260
                    for (size_t i = 0; i < sz; i++) {
2,103✔
1261
                        values.nullify_fwd_links(i, state);
1,824✔
1262
                    }
1,824✔
1263
                }
279✔
1264
            }
134,796✔
1265
            return IteratorControl::AdvanceToNext;
140,220✔
1266
        };
140,466✔
1267
        m_owner->for_each_and_every_column(remove_link_from_column);
17,394✔
1268
        return IteratorControl::AdvanceToNext;
17,394✔
1269
    };
17,394✔
1270

1271
    // Go through all clusters
1272
    traverse(func);
17,016✔
1273

1274
    const_cast<Table*>(get_owning_table())->remove_recursive(state);
17,016✔
1275
}
17,016✔
1276

1277
/**************************  ClusterTree::Iterator  **************************/
1278

1279
ClusterTree::Iterator::Iterator(const ClusterTree& t, size_t ndx)
1280
    : m_tree(t)
5,094,642✔
1281
    , m_leaf(0, t.get_alloc(), t)
5,094,642✔
1282
    , m_state(m_leaf)
5,094,642✔
1283
    , m_instance_version(t.get_instance_version())
5,094,642✔
1284
    , m_leaf_invalid(false)
5,094,642✔
1285
    , m_position(ndx)
5,094,642✔
1286
{
10,156,113✔
1287
    auto sz = t.size();
10,156,113✔
1288
    if (ndx >= sz) {
10,156,113✔
1289
        // end
1290
        m_position = sz;
9,599,145✔
1291
        m_leaf_invalid = true;
9,599,145✔
1292
    }
9,599,145✔
1293
    else if (ndx == 0) {
556,968✔
1294
        // begin
1295
        m_key = load_leaf(ObjKey(0));
543,609✔
1296
        m_leaf_start_pos = 0;
543,609✔
1297
    }
543,609✔
1298
    else {
13,359✔
1299
        auto s = const_cast<ClusterTree&>(m_tree).get(ndx, m_key);
13,359✔
1300
        m_state.init(s, m_key);
13,359✔
1301
        m_leaf_start_pos = ndx - m_state.m_current_index;
13,359✔
1302
    }
13,359✔
1303
}
10,156,113✔
1304

1305
ClusterTree::Iterator::Iterator(const Iterator& other)
1306
    : m_tree(other.m_tree)
336✔
1307
    , m_leaf(0, m_tree.get_alloc(), m_tree)
336✔
1308
    , m_state(m_leaf)
336✔
1309
    , m_instance_version(m_tree.get_instance_version())
336✔
1310
    , m_key(other.m_key)
336✔
1311
    , m_leaf_invalid(other.m_leaf_invalid)
336✔
1312
    , m_position(other.m_position)
336✔
1313
{
429✔
1314
    m_leaf_start_pos = m_position - m_state.m_current_index;
429✔
1315
}
429✔
1316

1317
size_t ClusterTree::Iterator::get_position()
1318
{
18,576✔
1319
    auto ndx = m_tree.get_ndx(m_key);
18,576✔
1320
    if (ndx == realm::npos) {
18,576✔
1321
        throw StaleAccessor("Stale iterator");
×
1322
    }
×
1323
    return ndx;
18,576✔
1324
}
18,576✔
1325

1326
ObjKey ClusterTree::Iterator::load_leaf(ObjKey key) const
1327
{
2,174,394✔
1328
    m_storage_version = m_tree.get_storage_version(m_instance_version);
2,174,394✔
1329
    // 'key' may or may not exist. If it does not exist, state is updated
1330
    // to point to the next object in line.
1331
    if (m_tree.get_leaf(key, m_state)) {
2,174,394✔
1332
        m_leaf_start_pos = m_position - m_state.m_current_index;
2,018,820✔
1333
        // Get the actual key value
1334
        return m_leaf.get_real_key(m_state.m_current_index);
2,018,820✔
1335
    }
2,018,820✔
1336
    else {
155,574✔
1337
        // end of table
1338
        return null_key;
155,574✔
1339
    }
155,574✔
1340
}
2,174,394✔
1341

1342
void ClusterTree::Iterator::go(size_t abs_pos)
1343
{
43,455✔
1344
    size_t sz = m_tree.size();
43,455✔
1345
    if (abs_pos >= sz) {
43,455✔
1346
        throw OutOfBounds("go() on Iterator", abs_pos, sz);
×
1347
    }
×
1348

1349
    m_position = abs_pos;
43,455✔
1350

1351
    // If the position is within the current leaf then just set the iterator to that position
1352
    if (!m_leaf_invalid && m_storage_version == m_tree.get_storage_version(m_instance_version)) {
43,455✔
1353
        if (abs_pos >= m_leaf_start_pos && abs_pos < (m_leaf_start_pos + m_leaf.node_size())) {
15,135✔
1354
            m_state.m_current_index = abs_pos - m_leaf_start_pos;
4,590✔
1355
            m_key = m_leaf.get_real_key(m_state.m_current_index);
4,590✔
1356
            return;
4,590✔
1357
        }
4,590✔
1358
    }
15,135✔
1359

1360
    // Find cluster holding requested position
1361
    auto s = m_tree.get(abs_pos, m_key);
38,865✔
1362
    m_state.init(s, m_key);
38,865✔
1363
    m_leaf_start_pos = abs_pos - s.index;
38,865✔
1364
    m_leaf_invalid = false;
38,865✔
1365
}
38,865✔
1366

1367
bool ClusterTree::Iterator::update() const
1368
{
21,893,574✔
1369
    if (m_leaf_invalid || m_storage_version != m_tree.get_storage_version(m_instance_version)) {
21,896,049✔
1370
        ObjKey k = load_leaf(m_key);
46,713✔
1371
        m_leaf_invalid = !k || (k != m_key);
46,713✔
1372
        if (m_leaf_invalid) {
46,713✔
1373
            throw StaleAccessor("Stale iterator");
30✔
1374
        }
30✔
1375
        return true;
46,683✔
1376
    }
46,713✔
1377

1378
    REALM_ASSERT(m_leaf.is_attached());
21,846,861✔
1379
    return false;
21,846,861✔
1380
}
21,893,574✔
1381

1382
ClusterTree::Iterator& ClusterTree::Iterator::operator++()
1383
{
20,411,208✔
1384
    if (m_leaf_invalid || m_storage_version != m_tree.get_storage_version(m_instance_version)) {
20,414,310✔
1385
        ObjKey k = load_leaf(m_key);
1,375,872✔
1386
        if (k != m_key) {
1,375,872✔
1387
            // Objects was deleted. k points to the next object
1388
            m_key = k;
60,894✔
1389
            m_leaf_invalid = !m_key;
60,894✔
1390
            return *this;
60,894✔
1391
        }
60,894✔
1392
    }
1,375,872✔
1393

1394
    m_state.m_current_index++;
20,350,314✔
1395
    m_position++;
20,350,314✔
1396
    if (m_state.m_current_index == m_leaf.node_size()) {
20,350,314✔
1397
        m_key = load_leaf(ObjKey(m_key.value + 1));
208,122✔
1398
        m_leaf_invalid = !m_key;
208,122✔
1399
    }
208,122✔
1400
    else {
20,142,192✔
1401
        m_key = m_leaf.get_real_key(m_state.m_current_index);
20,142,192✔
1402
    }
20,142,192✔
1403
    return *this;
20,350,314✔
1404
}
20,411,208✔
1405

1406
ClusterTree::Iterator& ClusterTree::Iterator::operator+=(ptrdiff_t adj)
1407
{
2,016✔
1408
    // If you have to jump far away and thus have to load many leaves,
1409
    // this function will be slow
1410
    REALM_ASSERT(adj >= 0);
2,016✔
1411
    if (adj == 0) {
2,016✔
1412
        return *this;
6✔
1413
    }
6✔
1414

1415
    size_t n = size_t(adj);
2,010✔
1416
    if (m_leaf_invalid || m_storage_version != m_tree.get_storage_version(m_instance_version)) {
2,010✔
1417
        ObjKey k = load_leaf(m_key);
12✔
1418
        if (k != m_key) {
12✔
1419
            // Objects was deleted. k points to the next object
1420
            m_key = k;
6✔
1421
            m_position = m_key ? m_tree.get_ndx(m_key) : m_tree.size();
6✔
1422
            n--;
6✔
1423
        }
6✔
1424
    }
12✔
1425
    if (n > 0) {
2,010✔
1426
        m_position += n;
2,010✔
1427
        size_t left_in_leaf = m_leaf.node_size() - m_state.m_current_index;
2,010✔
1428
        if (n < left_in_leaf) {
2,010✔
1429
            m_state.m_current_index += n;
1,986✔
1430
            m_key = m_leaf.get_real_key(m_state.m_current_index);
1,986✔
1431
        }
1,986✔
1432
        else {
24✔
1433
            if (m_position < m_tree.size()) {
24✔
1434
                auto s = const_cast<ClusterTree&>(m_tree).get(m_position, m_key);
18✔
1435
                m_state.init(s, m_key);
18✔
1436
                m_leaf_start_pos = m_position - m_state.m_current_index;
18✔
1437
            }
18✔
1438
            else {
6✔
1439
                m_key = ObjKey();
6✔
1440
                m_position = m_tree.size();
6✔
1441
            }
6✔
1442
        }
24✔
1443
    }
2,010✔
1444
    m_leaf_invalid = !m_key;
2,010✔
1445
    return *this;
2,010✔
1446
}
2,016✔
1447

1448
ClusterTree::Iterator::pointer ClusterTree::Iterator::operator->() const
1449
{
21,900,471✔
1450
    if (update() || m_key != m_obj.get_key()) {
21,900,471✔
1451
        new (&m_obj) Obj(m_tree.get_table_ref(), m_leaf.get_mem(), m_key, m_state.m_current_index);
18,027,903✔
1452
    }
18,027,903✔
1453

1454
    return &m_obj;
21,900,471✔
1455
}
21,900,471✔
1456

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