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

realm / realm-core / jorgen.edelbo_391

13 Aug 2024 07:57AM UTC coverage: 91.091% (-0.02%) from 91.107%
jorgen.edelbo_391

Pull #7826

Evergreen

web-flow
Merge pull request #7979 from realm/test-upgrade-files

Create test file in file-format 24
Pull Request #7826: Merge Next major

103486 of 182216 branches covered (56.79%)

3157 of 3519 new or added lines in 54 files covered. (89.71%)

191 existing lines in 22 files now uncovered.

219971 of 241486 relevant lines covered (91.09%)

6652643.8 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,434,352✔
66
        return false;
48,434,352✔
67
    }
48,434,352✔
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,719,493✔
79
        return Array::size() - s_first_node_index;
84,719,493✔
80
    }
84,719,493✔
81
    size_t get_tree_size() const override
82
    {
44,851,965✔
83
        return size_t(Array::get(s_sub_tree_size)) >> 1;
44,851,965✔
84
    }
44,851,965✔
85
    void set_tree_size(size_t sub_tree_size)
86
    {
42,072,537✔
87
        Array::set(s_sub_tree_size, sub_tree_size << 1 | 1);
42,072,537✔
88
    }
42,072,537✔
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,343✔
125
        REALM_ASSERT_DEBUG(ref == get_mem().get_ref());
104,343✔
126
        if (out.only_modified && m_alloc.is_read_only(ref)) {
104,343✔
NEW
127
            return ref;
×
NEW
128
        }
×
129
        REALM_ASSERT_DEBUG(get_is_inner_bptree_node_from_header(get_header()));
104,343✔
130
        REALM_ASSERT_DEBUG(!get_context_flag_from_header(get_header()));
104,343✔
131
        REALM_ASSERT_DEBUG(has_refs());
104,343✔
132
        TempArray written_node(size(), type_InnerBptreeNode);
104,343✔
133
        for (unsigned j = 0; j < size(); ++j) {
2,640,084✔
134
            RefOrTagged rot = get_as_ref_or_tagged(j);
2,535,741✔
135
            if (rot.is_ref() && rot.get_as_ref()) {
2,535,741✔
136
                if (out.only_modified && m_alloc.is_read_only(rot.get_as_ref())) {
2,303,634✔
137
                    written_node.set(j, rot);
1,923,615✔
138
                    continue;
1,923,615✔
139
                }
1,923,615✔
140
                if (j == 0) {
380,019✔
141
                    // keys (ArrayUnsigned, me thinks)
142
                    Array array_unsigned(m_alloc);
1,029✔
143
                    array_unsigned.init_from_ref(rot.get_as_ref());
1,029✔
144
                    written_node.set_as_ref(j, array_unsigned.write(out, false, out.only_modified, false));
1,029✔
145
                }
1,029✔
146
                else {
378,990✔
147
                    auto header = m_alloc.translate(rot.get_as_ref());
378,990✔
148
                    MemRef m(header, rot.get_as_ref(), m_alloc);
378,990✔
149
                    if (get_is_inner_bptree_node_from_header(header)) {
378,990✔
150
                        ClusterNodeInner inner_node(m_alloc, m_tree_top);
7,464✔
151
                        inner_node.init(m);
7,464✔
152
                        written_node.set_as_ref(j, inner_node.typed_write(rot.get_as_ref(), out));
7,464✔
153
                    }
7,464✔
154
                    else {
371,526✔
155
                        Cluster cluster(j, m_alloc, m_tree_top);
371,526✔
156
                        cluster.init(m);
371,526✔
157
                        written_node.set_as_ref(j, cluster.typed_write(rot.get_as_ref(), out));
371,526✔
158
                    }
371,526✔
159
                }
378,990✔
160
            }
380,019✔
161
            else { // not a ref, just copy value over
232,107✔
162
                written_node.set(j, rot);
232,107✔
163
            }
232,107✔
164
        }
2,535,741✔
165
        return written_node.write(out);
104,343✔
166
    }
104,343✔
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
    {
391,820,373✔
185
        if (m_keys.is_attached()) {
391,820,373✔
186
            auto upper = m_keys.upper_bound(key.value);
335,440,464✔
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) {
335,440,464✔
191
                return false;
×
192
            }
×
193
            ret.ndx = upper - 1;
335,440,464✔
194
            ret.offset = m_keys.get(ret.ndx);
335,440,464✔
195
        }
335,440,464✔
196
        else {
56,379,909✔
197
            size_t sz = node_size();
56,379,909✔
198
            REALM_ASSERT_DEBUG(sz > 0);
56,379,909✔
199
            size_t max_ndx = sz - 1;
56,379,909✔
200
            ret.ndx = std::min(size_t(key.value >> m_shift_factor), max_ndx);
56,379,909✔
201
            ret.offset = ret.ndx << m_shift_factor;
56,379,909✔
202
        }
56,379,909✔
203
        ret.key = RowKey(key.value - ret.offset);
391,820,373✔
204
        ref_type child_ref = _get_child_ref(ret.ndx);
391,820,373✔
205
        char* child_header = m_alloc.translate(child_ref);
391,820,373✔
206
        ret.mem = MemRef(child_header, child_ref, m_alloc);
391,820,373✔
207
        return true;
391,820,373✔
208
    }
391,820,373✔
209

210
    ref_type _get_child_ref(size_t ndx) const noexcept
211
    {
469,769,928✔
212
        return Array::get_as_ref(ndx + s_first_node_index);
469,769,928✔
213
    }
469,769,928✔
214
    void _insert_child_ref(size_t ndx, ref_type ref)
215
    {
92,736✔
216
        Array::insert(ndx + s_first_node_index, from_ref(ref));
92,736✔
217
    }
92,736✔
218
    void _erase_child_ref(size_t ndx)
219
    {
21,192✔
220
        Array::erase(ndx + s_first_node_index);
21,192✔
221
    }
21,192✔
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,873,474✔
241
{
97,748,937✔
242
}
97,748,937✔
243

244
ClusterNodeInner::~ClusterNodeInner() {}
97,735,665✔
245

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

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

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

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

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

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

293
template <class T, class F>
294
T ClusterNodeInner::recurse(ChildInfo& child_info, F func)
295
{
390,472,887✔
296
    bool child_is_leaf = !Array::get_is_inner_bptree_node_from_header(child_info.mem.get_addr());
390,472,887✔
297
    if (child_is_leaf) {
390,472,887✔
298
        Cluster leaf(child_info.offset + m_offset, m_alloc, m_tree_top);
301,172,307✔
299
        leaf.set_parent(this, child_info.ndx + s_first_node_index);
301,172,307✔
300
        leaf.init(child_info.mem);
301,172,307✔
301
        return func(&leaf, child_info);
301,172,307✔
302
    }
301,172,307✔
303
    else {
89,300,580✔
304
        ClusterNodeInner node(m_alloc, m_tree_top);
89,300,580✔
305
        node.set_parent(this, child_info.ndx + s_first_node_index);
89,300,580✔
306
        node.init(child_info.mem);
89,300,580✔
307
        node.set_offset(child_info.offset + m_offset);
89,300,580✔
308
        return func(&node, child_info);
89,300,580✔
309
    }
89,300,580✔
310
}
390,472,887✔
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,948✔
321
    ChildInfo child_info;
486,948✔
322
    if (!find_child(key, child_info)) {
486,948✔
323
        throw KeyNotFound("Child not found in update_ref_in_parent");
×
324
    }
×
325
    if (this->m_sub_tree_depth == 1) {
486,948✔
326
        set(child_info.ndx + s_first_node_index, ref);
270,006✔
327
    }
270,006✔
328
    else {
216,942✔
329
        ClusterNodeInner node(m_alloc, m_tree_top);
216,942✔
330
        node.set_parent(this, child_info.ndx + s_first_node_index);
216,942✔
331
        node.init(child_info.mem);
216,942✔
332
        node.set_offset(child_info.offset + m_offset);
216,942✔
333
        node.update_ref_in_parent(child_info.key, ref);
216,942✔
334
    }
216,942✔
335
}
486,948✔
336

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

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

344
        if (!new_sibling_ref) {
33,684,213✔
345
            return ref_type(0);
33,600,591✔
346
        }
33,600,591✔
347

348
        size_t new_ref_ndx = child_info.ndx + 1;
83,622✔
349

350
        int64_t split_key_value = state.split_key + child_info.offset;
83,622✔
351
        uint64_t sz = node_size();
83,622✔
352
        if (sz < cluster_node_size) {
94,299✔
353
            if (m_keys.is_attached()) {
92,736✔
354
                m_keys.insert(new_ref_ndx, split_key_value);
82,668✔
355
            }
82,668✔
356
            else {
10,068✔
357
                if (uint64_t(split_key_value) != sz << m_shift_factor) {
10,068✔
358
                    ensure_general_form();
288✔
359
                    m_keys.insert(new_ref_ndx, split_key_value);
288✔
360
                }
288✔
361
            }
10,068✔
362
            _insert_child_ref(new_ref_ndx, new_sibling_ref);
92,736✔
363
            return ref_type(0);
92,736✔
364
        }
92,736✔
365

366
        ClusterNodeInner child(m_alloc, m_tree_top);
2,147,485,210✔
367
        child.create(m_sub_tree_depth);
2,147,485,210✔
368
        if (new_ref_ndx == sz) {
2,147,485,210✔
369
            child.add(new_sibling_ref);
180✔
370
            state.split_key = split_key_value;
180✔
371
        }
180✔
372
        else {
2,147,485,120✔
373
            int64_t first_key_value = m_keys.get(new_ref_ndx);
2,147,485,120✔
374
            child.ensure_general_form();
2,147,485,120✔
375
            move(new_ref_ndx, &child, first_key_value);
2,147,485,120✔
376
            add(new_sibling_ref, split_key_value); // Throws
2,147,485,120✔
377
            state.split_key = first_key_value;
2,147,485,120✔
378
        }
2,147,485,120✔
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,485,210✔
382
        set_tree_size(get_tree_size() - child_sub_tree_size);
2,147,485,210✔
383

384
        return child.get_ref();
2,147,485,210✔
385
    });
83,622✔
386
}
33,754,608✔
387

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

400
ObjKey ClusterNodeInner::get(size_t ndx, ClusterNode::State& state) const
401
{
12,000,831✔
402
    size_t sz = node_size();
12,000,831✔
403
    size_t child_ndx = 0;
12,000,831✔
404
    while (child_ndx < sz) {
73,619,544✔
405
        int64_t key_offset = m_keys.is_attached() ? m_keys.get(child_ndx) : (child_ndx << m_shift_factor);
73,598,532✔
406

407
        ref_type child_ref = _get_child_ref(child_ndx);
73,598,532✔
408
        char* child_header = m_alloc.translate(child_ref);
73,598,532✔
409
        bool child_is_leaf = !Array::get_is_inner_bptree_node_from_header(child_header);
73,598,532✔
410
        size_t sub_tree_size;
73,598,532✔
411
        if (child_is_leaf) {
73,598,532✔
412
            sub_tree_size = Cluster::node_size_from_header(m_alloc, child_header);
70,964,082✔
413
            if (ndx < sub_tree_size) {
70,964,082✔
414
                Cluster leaf(key_offset + m_offset, m_alloc, m_tree_top);
9,393,180✔
415
                leaf.init(MemRef(child_header, child_ref, m_alloc));
9,393,180✔
416
                REALM_ASSERT(sub_tree_size == leaf.get_tree_size());
9,393,180✔
417
                return leaf.get(ndx, state);
9,393,180✔
418
            }
9,393,180✔
419
        }
70,964,082✔
420
        else {
2,634,450✔
421
            ClusterNodeInner node(m_alloc, m_tree_top);
2,634,450✔
422
            node.init(MemRef(child_header, child_ref, m_alloc));
2,634,450✔
423
            node.set_offset(key_offset + m_offset);
2,634,450✔
424
            sub_tree_size = node.get_tree_size();
2,634,450✔
425
            if (ndx < sub_tree_size) {
2,658,828✔
426
                return node.get(ndx, state);
2,588,964✔
427
            }
2,588,964✔
428
        }
2,634,450✔
429
        child_ndx++;
61,616,388✔
430
        ndx -= sub_tree_size;
61,616,388✔
431
    }
61,616,388✔
432
    return {};
2,147,504,659✔
433
}
12,000,831✔
434

435
size_t ClusterNodeInner::get_ndx(RowKey key, size_t ndx) const noexcept
436
{
24,726✔
437
    ChildInfo child_info;
24,726✔
438
    if (!find_child(key, child_info)) {
24,726✔
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,726✔
446
    if (child_is_leaf) {
24,726✔
447
        for (unsigned i = 0; i < child_info.ndx; i++) {
51,078✔
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,726✔
453
        leaf.init(child_info.mem);
24,726✔
454
        return leaf.get_ndx(child_info.key, ndx);
24,726✔
455
    }
24,726✔
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,726✔
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,435,190✔
500
    return recurse<size_t>(key, [this, &state](ClusterNode* erase_node, ChildInfo& child_info) {
8,435,196✔
501
        size_t erase_node_size = erase_node->erase(child_info.key, state);
8,434,575✔
502
        bool is_leaf = erase_node->is_leaf();
8,434,575✔
503
        set_tree_size(get_tree_size() - 1);
8,434,575✔
504

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

508
            ensure_general_form();
13,344✔
509
            _erase_child_ref(child_info.ndx);
13,344✔
510
            m_keys.erase(child_info.ndx);
13,344✔
511
            if (child_info.ndx == 0 && m_keys.size() > 0) {
13,344✔
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,344✔
519
        else if (erase_node_size < cluster_node_size / 2 && child_info.ndx < (node_size() - 1)) {
8,421,231✔
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,315,528✔
523
            Cluster l2(child_info.offset, m_alloc, m_tree_top);
3,315,528✔
524
            ClusterNodeInner n2(m_alloc, m_tree_top);
3,315,528✔
525
            ClusterNode* sibling_node = is_leaf ? static_cast<ClusterNode*>(&l2) : static_cast<ClusterNode*>(&n2);
3,315,528✔
526
            sibling_node->set_parent(this, sibling_ndx + s_first_node_index);
3,315,528✔
527
            sibling_node->init_from_parent();
3,315,528✔
528

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

531
            if (combined_size < cluster_node_size * 3 / 4) {
3,315,528✔
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,848✔
535
                                                       : 0 - (1 << m_shift_factor);
7,848✔
536
                // And then move all elements into current node
537
                sibling_node->ensure_general_form();
7,848✔
538
                erase_node->ensure_general_form();
7,848✔
539
                sibling_node->move(0, erase_node, key_adj);
7,848✔
540

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

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

548
                ensure_general_form();
7,848✔
549
                _erase_child_ref(sibling_ndx);
7,848✔
550
                m_keys.erase(sibling_ndx);
7,848✔
551
            }
7,848✔
552
        }
3,315,528✔
553

554
        return node_size();
8,434,575✔
555
    });
8,434,575✔
556
}
8,435,190✔
557

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

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

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

586
void ClusterNodeInner::remove_column(ColKey col)
587
{
9✔
588
    size_t sz = node_size();
9✔
589
    for (size_t i = 0; i < sz; i++) {
27✔
590
        std::shared_ptr<ClusterNode> node = m_tree_top.get_node(this, i + s_first_node_index);
18✔
591
        node->remove_column(col);
18✔
592
    }
18✔
593
}
9✔
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,873✔
615
    if (m_keys.is_attached()) {
6,873✔
616
        m_keys.add(key_value);
33✔
617
    }
33✔
618
    else {
6,840✔
619
        if (uint64_t(key_value) != (uint64_t(node_size()) << m_shift_factor)) {
6,840✔
620
            ensure_general_form();
2,793✔
621
            m_keys.add(key_value);
2,793✔
622
        }
2,793✔
623
    }
6,840✔
624
    Array::add(from_ref(ref));
6,873✔
625
}
6,873✔
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,696,595✔
631
    size_t child_ndx;
2,696,595✔
632
    if (m_keys.is_attached()) {
2,696,595✔
633
        child_ndx = m_keys.upper_bound(key.value);
2,658,771✔
634
        if (child_ndx > 0)
2,658,771✔
635
            child_ndx--;
2,659,590✔
636
    }
2,658,771✔
637
    else {
37,824✔
638
        REALM_ASSERT_DEBUG(node_size() > 0);
37,824✔
639
        size_t max_ndx = node_size() - 1;
37,824✔
640
        child_ndx = std::min(size_t(key.value >> m_shift_factor), max_ndx);
37,824✔
641
    }
37,824✔
642

643
    size_t sz = node_size();
2,696,595✔
644
    while (child_ndx < sz) {
2,752,293✔
645
        uint64_t key_offset = m_keys.is_attached() ? m_keys.get(child_ndx) : (child_ndx << m_shift_factor);
2,744,358✔
646
        RowKey new_key(key_offset < key.value ? key.value - key_offset : 0);
2,744,358✔
647
        state.m_key_offset += key_offset;
2,744,358✔
648

649
        ref_type child_ref = _get_child_ref(child_ndx);
2,744,358✔
650
        char* child_header = m_alloc.translate(child_ref);
2,744,358✔
651
        bool child_is_leaf = !Array::get_is_inner_bptree_node_from_header(child_header);
2,744,358✔
652
        if (child_is_leaf) {
2,744,358✔
653
            state.m_current_leaf.init(MemRef(child_header, child_ref, m_alloc));
1,539,423✔
654
            state.m_current_leaf.set_offset(state.m_key_offset);
1,539,423✔
655
            state.m_current_index = state.m_current_leaf.lower_bound_key(new_key);
1,539,423✔
656
            if (state.m_current_index < state.m_current_leaf.node_size())
1,539,423✔
657
                return true;
1,483,968✔
658
        }
1,539,423✔
659
        else {
1,204,935✔
660
            ClusterNodeInner node(m_alloc, m_tree_top);
1,204,935✔
661
            node.init(MemRef(child_header, child_ref, m_alloc));
1,204,935✔
662
            if (node.get_leaf(new_key, state))
1,204,935✔
663
                return true;
1,204,692✔
664
        }
1,204,935✔
665
        state.m_key_offset -= key_offset;
55,698✔
666
        child_ndx++;
55,698✔
667
    }
55,698✔
668
    return false;
7,935✔
669
}
2,696,595✔
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++) {
8,748✔
697
        new_cluster_node_inner->Array::add(_get_child_ref(i));
8,670✔
698
    }
8,670✔
699
    for (size_t i = ndx; i < m_keys.size(); i++) {
8,748✔
700
        new_cluster_node_inner->m_keys.add(m_keys.get(i) - key_adj);
8,670✔
701
    }
8,670✔
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,588✔
710
    size_t sub_tree_size = 0;
3,588✔
711
    auto sz = node_size();
3,588✔
712

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

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

732
    for (unsigned i = 0; i < sz; i++) {
8,442,225✔
733
        ref_type ref = _get_child_ref(i);
6,798,174✔
734
        char* header = m_alloc.translate(ref);
6,798,174✔
735
        bool child_is_leaf = !Array::get_is_inner_bptree_node_from_header(header);
6,798,174✔
736
        MemRef mem(header, ref, m_alloc);
6,798,174✔
737
        int64_t offs = (m_keys.is_attached() ? m_keys.get(i) : i << m_shift_factor) + key_offset;
6,798,174✔
738
        if (child_is_leaf) {
6,798,174✔
739
            Cluster leaf(offs, m_alloc, m_tree_top);
6,586,116✔
740
            leaf.init(mem);
6,586,116✔
741
            if (func(&leaf) == IteratorControl::Stop) {
6,586,116✔
742
                return true;
29,130✔
743
            }
29,130✔
744
        }
6,586,116✔
745
        else {
212,058✔
746
            ClusterNodeInner node(m_alloc, m_tree_top);
212,058✔
747
            node.init(mem);
212,058✔
748
            if (node.traverse(func, offs)) {
212,058✔
749
                return true;
×
750
            }
×
751
        }
212,058✔
752
    }
6,798,174✔
753
    return false;
1,644,051✔
754
}
1,673,181✔
755

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

760
    for (unsigned i = 0; i < sz; i++) {
477✔
761
        ref_type ref = _get_child_ref(i);
438✔
762
        char* header = m_alloc.translate(ref);
438✔
763
        bool child_is_leaf = !Array::get_is_inner_bptree_node_from_header(header);
438✔
764
        MemRef mem(header, ref, m_alloc);
438✔
765
        int64_t offs = (m_keys.is_attached() ? m_keys.get(i) : i << m_shift_factor) + key_offset;
438✔
766
        if (child_is_leaf) {
438✔
767
            Cluster leaf(offs, m_alloc, m_tree_top);
438✔
768
            leaf.init(mem);
438✔
769
            leaf.set_parent(this, i + s_first_node_index);
438✔
770
            func(&leaf);
438✔
771
        }
438✔
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
    }
438✔
779
}
39✔
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)
31,326✔
804
    , m_owner(owner)
31,326✔
805
    , m_top_position_for_cluster_tree(top_position_for_cluster_tree)
31,326✔
806
{
68,658✔
807
}
68,658✔
808

809
ClusterTree::~ClusterTree() {}
38,277✔
810

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

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

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

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

838
    return new_root;
150,090✔
839
}
2,341,971✔
840

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

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

847
    char* child_header = static_cast<char*>(m_alloc.translate(ref));
24,384✔
848
    bool child_is_leaf = !Array::get_is_inner_bptree_node_from_header(child_header);
24,384✔
849
    if (child_is_leaf) {
24,384✔
850
        node = std::make_unique<Cluster>(0, m_alloc, *this);
24,270✔
851
    }
24,270✔
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,384✔
856
    node->set_parent(parent, ndx_in_parent);
24,384✔
857

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

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

865
    if (state.m_group) {
18,195✔
866
        remove_all_links(state); // This will also delete objects loosing their last strong link
17,043✔
867
    }
17,043✔
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,195✔
872
        // Go through all clusters
873
        traverse([repl, this](const Cluster* cluster) {
15,888✔
874
            auto sz = cluster->node_size();
15,888✔
875
            for (size_t i = 0; i < sz; i++) {
118,203✔
876
                repl->remove_object(m_owner, cluster->get_real_key(i));
102,315✔
877
            }
102,315✔
878
            return IteratorControl::AdvanceToNext;
15,888✔
879
        });
15,888✔
880
    }
15,534✔
881

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

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

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

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

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

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

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

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

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

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

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

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

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

943
bool ClusterTree::init_from_parent()
944
{
2,340,585✔
945
    m_root = get_root_from_parent();
2,340,585✔
946
    if (m_root) {
2,342,616✔
947
        m_size = m_root->get_tree_size();
2,342,616✔
948
        return true;
2,342,616✔
949
    }
2,342,616✔
950
    m_size = 0;
2,147,483,647✔
951
    return false;
2,147,483,647✔
952
}
2,340,585✔
953

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

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

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

971
        replace_root(std::move(new_root));
3,330✔
972
    }
3,330✔
973
    m_size++;
25,088,052✔
974
}
25,088,052✔
975

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

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

983
    bump_content_version();
25,063,398✔
984
    bump_storage_version();
25,063,398✔
985

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

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

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

1003
    ClusterNode::State state;
85,617,387✔
1004
    return m_root->try_get(ClusterNode::RowKey(k), state);
85,617,387✔
1005
}
85,825,314✔
1006

1007
ClusterNode::State ClusterTree::try_get(ObjKey k) const noexcept
1008
{
28,117,656✔
1009
    ClusterNode::State state;
28,117,656✔
1010
    if (!(k && m_root->try_get(ClusterNode::RowKey(k), state)))
28,214,820✔
1011
        state.index = realm::npos;
53,847✔
1012
    return state;
28,117,656✔
1013
}
28,117,656✔
1014

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

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

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

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

1044
    bump_content_version();
5,323,233✔
1045
    bump_storage_version();
5,323,233✔
1046
    m_size--;
5,323,233✔
1047
    while (!m_root->is_leaf() && root_size == 1) {
5,323,473✔
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,233✔
1058

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

1078
bool ClusterTree::traverse(TraverseFunction func) const
1079
{
2,434,284✔
1080
    if (m_root->is_leaf()) {
2,434,284✔
1081
        return func(static_cast<Cluster*>(m_root.get())) == IteratorControl::Stop;
757,299✔
1082
    }
757,299✔
1083
    else {
1,676,985✔
1084
        return static_cast<ClusterNodeInner*>(m_root.get())->traverse(func, 0);
1,676,985✔
1085
    }
1,676,985✔
1086
}
2,434,284✔
1087

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

1098
void ClusterTree::set_spec(ArrayPayload& arr, ColKey::Idx col_ndx) const
1099
{
3,947,169✔
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,947,358✔
1103
        auto spec_ndx = m_owner->leaf_ndx2spec_ndx(col_ndx);
3,947,358✔
1104
        arr.set_spec(&m_owner->m_spec, spec_ndx);
3,947,358✔
1105
    }
3,947,358✔
1106
}
3,947,169✔
1107

1108
TableRef ClusterTree::get_table_ref() const
1109
{
243,711,633✔
1110
    REALM_ASSERT(m_owner != nullptr);
243,711,633✔
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;
243,711,633✔
1114
}
243,711,633✔
1115

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

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

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

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

1143
void ClusterTree::remove_all_links(CascadeState& state)
1144
{
17,043✔
1145
    Allocator& alloc = get_alloc();
17,043✔
1146
    auto origin_table = get_owning_table();
17,043✔
1147
    // This function will add objects that should be deleted to 'state'
1148
    auto func = [&](const Cluster* cluster) {
17,421✔
1149
        auto remove_link_from_column = [&](ColKey col_key) {
140,550✔
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,550✔
1154
                return IteratorControl::AdvanceToNext;
288✔
1155
            }
288✔
1156
            auto col_type = col_key.get_type();
140,262✔
1157
            if (col_key.is_collection()) {
140,262✔
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,838✔
1220
                if (col_type == col_type_Link) {
134,838✔
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,332✔
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,332✔
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,224✔
1255
                    ArrayBacklink values(alloc);
276✔
1256
                    cluster->init_leaf(col_key, &values);
276✔
1257
                    values.set_parent(const_cast<Cluster*>(cluster),
276✔
1258
                                      col_key.get_index().val + Cluster::s_first_col_index);
276✔
1259
                    size_t sz = values.size();
276✔
1260
                    for (size_t i = 0; i < sz; i++) {
2,100✔
1261
                        values.nullify_fwd_links(i, state);
1,824✔
1262
                    }
1,824✔
1263
                }
276✔
1264
            }
134,838✔
1265
            return IteratorControl::AdvanceToNext;
140,262✔
1266
        };
140,550✔
1267
        m_owner->for_each_and_every_column(remove_link_from_column);
17,421✔
1268
        return IteratorControl::AdvanceToNext;
17,421✔
1269
    };
17,421✔
1270

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

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

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

1279
ClusterTree::Iterator::Iterator(const ClusterTree& t, size_t ndx)
1280
    : m_tree(t)
5,023,128✔
1281
    , m_leaf(0, t.get_alloc(), t)
5,023,128✔
1282
    , m_state(m_leaf)
5,023,128✔
1283
    , m_instance_version(t.get_instance_version())
5,023,128✔
1284
    , m_leaf_invalid(false)
5,023,128✔
1285
    , m_position(ndx)
5,023,128✔
1286
{
10,007,646✔
1287
    auto sz = t.size();
10,007,646✔
1288
    if (ndx >= sz) {
10,007,646✔
1289
        // end
1290
        m_position = sz;
9,413,271✔
1291
        m_leaf_invalid = true;
9,413,271✔
1292
    }
9,413,271✔
1293
    else if (ndx == 0) {
594,375✔
1294
        // begin
1295
        m_key = load_leaf(ObjKey(0));
579,072✔
1296
        m_leaf_start_pos = 0;
579,072✔
1297
    }
579,072✔
1298
    else {
15,303✔
1299
        auto s = const_cast<ClusterTree&>(m_tree).get(ndx, m_key);
15,303✔
1300
        m_state.init(s, m_key);
15,303✔
1301
        m_leaf_start_pos = ndx - m_state.m_current_index;
15,303✔
1302
    }
15,303✔
1303
}
10,007,646✔
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,642✔
1319
    auto ndx = m_tree.get_ndx(m_key);
18,642✔
1320
    if (ndx == realm::npos) {
18,642✔
1321
        throw StaleAccessor("Stale iterator");
×
1322
    }
×
1323
    return ndx;
18,642✔
1324
}
18,642✔
1325

1326
ObjKey ClusterTree::Iterator::load_leaf(ObjKey key) const
1327
{
2,216,913✔
1328
    m_storage_version = m_tree.get_storage_version(m_instance_version);
2,216,913✔
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,216,913✔
1332
        m_leaf_start_pos = m_position - m_state.m_current_index;
2,055,666✔
1333
        // Get the actual key value
1334
        return m_leaf.get_real_key(m_state.m_current_index);
2,055,666✔
1335
    }
2,055,666✔
1336
    else {
161,247✔
1337
        // end of table
1338
        return null_key;
161,247✔
1339
    }
161,247✔
1340
}
2,216,913✔
1341

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

1349
    m_position = abs_pos;
43,464✔
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,464✔
1353
        if (abs_pos >= m_leaf_start_pos && abs_pos < (m_leaf_start_pos + m_leaf.node_size())) {
15,138✔
1354
            m_state.m_current_index = abs_pos - m_leaf_start_pos;
4,608✔
1355
            m_key = m_leaf.get_real_key(m_state.m_current_index);
4,608✔
1356
            return;
4,608✔
1357
        }
4,608✔
1358
    }
15,138✔
1359

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

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

1378
    REALM_ASSERT(m_leaf.is_attached());
21,286,233✔
1379
    return false;
21,286,233✔
1380
}
21,332,811✔
1381

1382
ClusterTree::Iterator& ClusterTree::Iterator::operator++()
1383
{
20,032,035✔
1384
    if (m_leaf_invalid || m_storage_version != m_tree.get_storage_version(m_instance_version)) {
20,033,691✔
1385
        ObjKey k = load_leaf(m_key);
1,382,991✔
1386
        if (k != m_key) {
1,382,991✔
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,382,991✔
1393

1394
    m_state.m_current_index++;
19,971,141✔
1395
    m_position++;
19,971,141✔
1396
    if (m_state.m_current_index == m_leaf.node_size()) {
19,971,141✔
1397
        m_key = load_leaf(ObjKey(m_key.value + 1));
208,443✔
1398
        m_leaf_invalid = !m_key;
208,443✔
1399
    }
208,443✔
1400
    else {
19,762,698✔
1401
        m_key = m_leaf.get_real_key(m_state.m_current_index);
19,762,698✔
1402
    }
19,762,698✔
1403
    return *this;
19,971,141✔
1404
}
20,032,035✔
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,338,982✔
1450
    if (update() || m_key != m_obj.get_key()) {
21,338,982✔
1451
        new (&m_obj) Obj(m_tree.get_table_ref(), m_leaf.get_mem(), m_key, m_state.m_current_index);
17,766,318✔
1452
    }
17,766,318✔
1453

1454
    return &m_obj;
21,338,982✔
1455
}
21,338,982✔
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