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

realm / realm-core / jorgen.edelbo_402

21 Aug 2024 11:10AM UTC coverage: 91.054% (-0.03%) from 91.085%
jorgen.edelbo_402

Pull #7803

Evergreen

jedelbo
Small fix to Table::typed_write

When writing the realm to a new file from a write transaction,
the Table may be COW so that the top ref is changed. So don't
use the ref that is present in the group when the operation starts.
Pull Request #7803: Feature/string compression

103494 of 181580 branches covered (57.0%)

1929 of 1999 new or added lines in 46 files covered. (96.5%)

695 existing lines in 51 files now uncovered.

220142 of 241772 relevant lines covered (91.05%)

7344461.76 hits per line

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

77.7
/src/realm/cluster.cpp
1
/*************************************************************************
2
 *
3
 * Copyright 2016 Realm Inc.
4
 *
5
 * Licensed under the Apache License, Version 2.0 (the "License");
6
 * you may not use this file except in compliance with the License.
7
 * You may obtain a copy of the License at
8
 *
9
 * http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 *
17
 **************************************************************************/
18

19
#include "realm/cluster.hpp"
20
#include "realm/array_integer.hpp"
21
#include "realm/array_basic.hpp"
22
#include "realm/array_bool.hpp"
23
#include "realm/array_string.hpp"
24
#include "realm/array_binary.hpp"
25
#include "realm/array_mixed.hpp"
26
#include "realm/array_timestamp.hpp"
27
#include "realm/array_decimal128.hpp"
28
#include "realm/array_fixed_bytes.hpp"
29
#include "realm/array_key.hpp"
30
#include "realm/array_ref.hpp"
31
#include "realm/array_typed_link.hpp"
32
#include "realm/array_backlink.hpp"
33
#include "realm/column_type_traits.hpp"
34
#include "realm/replication.hpp"
35
#include "realm/dictionary.hpp"
36
#include "realm/list.hpp"
37
#include <iostream>
38
#include <cmath>
39

40
namespace realm {
41

42
/******************************* FieldValues *********************************/
43

44
FieldValues::FieldValues(std::initializer_list<FieldValue> init)
45
    : m_values(init)
539,499✔
46
{
1,101,033✔
47
    if (m_values.size() > 1) {
1,101,033✔
48
        // Sort according to ColKey index
49
        std::sort(m_values.begin(), m_values.end(), [](const auto& a, const auto& b) {
35,679✔
50
            return a.col_key.get_index().val < b.col_key.get_index().val;
35,679✔
51
        });
35,679✔
52
    }
11,928✔
53
}
1,101,033✔
54

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

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

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

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

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

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

97

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

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

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

109

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

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

125
    auto column_initialize = [this](ColKey col_key) {
194,586✔
126
        auto col_ndx = col_key.get_index();
194,586✔
127
        while (size() <= col_ndx.val + 1)
389,172✔
128
            add(0);
194,586✔
129
        auto type = col_key.get_type();
194,586✔
130
        auto attr = col_key.get_attrs();
194,586✔
131
        if (attr.test(col_attr_Collection)) {
194,586✔
132
            ArrayRef arr(m_alloc);
6,765✔
133
            arr.create();
6,765✔
134
            arr.set_parent(this, col_ndx.val + s_first_col_index);
6,765✔
135
            arr.update_parent();
6,765✔
136
            return IteratorControl::AdvanceToNext;
6,765✔
137
        }
6,765✔
138
        switch (type) {
187,821✔
139
            case col_type_Int:
114,522✔
140
                if (attr.test(col_attr_Nullable)) {
114,522✔
141
                    do_create<ArrayIntNull>(col_key);
13,698✔
142
                }
13,698✔
143
                else {
100,824✔
144
                    do_create<ArrayInteger>(col_key);
100,824✔
145
                }
100,824✔
146
                break;
114,522✔
147
            case col_type_Bool:
687✔
148
                do_create<ArrayBoolNull>(col_key);
687✔
149
                break;
687✔
150
            case col_type_Float:
1,128✔
151
                do_create<ArrayFloatNull>(col_key);
1,128✔
152
                break;
1,128✔
153
            case col_type_Double:
5,790✔
154
                do_create<ArrayDoubleNull>(col_key);
5,790✔
155
                break;
5,790✔
156
            case col_type_String: {
45,351✔
157
                do_create<ArrayString>(col_key);
45,351✔
158
                break;
45,351✔
159
            }
×
160
            case col_type_Binary:
5,280✔
161
                do_create<ArrayBinary>(col_key);
5,280✔
162
                break;
5,280✔
163
            case col_type_Mixed:
636✔
164
                do_create<ArrayMixed>(col_key);
636✔
165
                break;
636✔
166
            case col_type_Timestamp:
4,851✔
167
                do_create<ArrayTimestamp>(col_key);
4,851✔
168
                break;
4,851✔
169
            case col_type_Decimal:
276✔
170
                do_create<ArrayDecimal128>(col_key);
276✔
171
                break;
276✔
172
            case col_type_ObjectId:
2,937✔
173
                do_create<ArrayObjectIdNull>(col_key);
2,937✔
174
                break;
2,937✔
175
            case col_type_UUID:
408✔
176
                do_create<ArrayUUIDNull>(col_key);
408✔
177
                break;
408✔
178
            case col_type_Link:
2,271✔
179
                do_create<ArrayKey>(col_key);
2,271✔
180
                break;
2,271✔
181
            case col_type_TypedLink:
✔
182
                do_create<ArrayTypedLink>(col_key);
×
183
                break;
×
184
            case col_type_BackLink:
3,687✔
185
                do_create<ArrayBacklink>(col_key);
3,687✔
186
                break;
3,687✔
187
            default:
✔
188
                REALM_UNREACHABLE();
189
        }
187,821✔
190
        return IteratorControl::AdvanceToNext;
187,824✔
191
    };
187,821✔
192
    m_tree_top.m_owner->for_each_and_every_column(column_initialize);
100,353✔
193

194
    // By specifying the minimum size, we ensure that the array has a capacity
195
    // to hold m_size 64 bit refs.
196
    ensure_size(m_size * 8);
100,353✔
197
    // "ensure_size" may COW, but as array is just created, it has no parents, so
198
    // failing to update parent is not an error.
199
    clear_missing_parent_update();
100,353✔
200
}
100,353✔
201

202
void Cluster::init(MemRef mem)
203
{
332,351,850✔
204
    Array::init_from_mem(mem);
332,351,850✔
205
    auto rot = Array::get_as_ref_or_tagged(0);
332,351,850✔
206
    if (rot.is_tagged()) {
332,351,850✔
207
        m_keys.detach();
268,392,816✔
208
    }
268,392,816✔
209
    else {
63,959,034✔
210
        m_keys.init_from_ref(rot.get_as_ref());
63,959,034✔
211
    }
63,959,034✔
212
}
332,351,850✔
213

214
void Cluster::update_from_parent() noexcept
215
{
1,020,687✔
216
    Array::update_from_parent();
1,020,687✔
217
    auto rot = Array::get_as_ref_or_tagged(0);
1,020,687✔
218
    if (!rot.is_tagged()) {
1,020,687✔
219
        m_keys.update_from_parent();
39,084✔
220
    }
39,084✔
221
}
1,020,687✔
222

223
MemRef Cluster::ensure_writeable(RowKey)
224
{
×
225
    // By specifying the minimum size, we ensure that the array has a capacity
226
    // to hold m_size 64 bit refs.
227
    copy_on_write(8 * m_size);
×
228

229
    return get_mem();
×
230
}
×
231

232
void Cluster::update_ref_in_parent(RowKey, ref_type)
233
{
×
234
    REALM_UNREACHABLE();
235
}
×
236

237
size_t Cluster::node_size_from_header(Allocator& alloc, const char* header)
238
{
72,141,411✔
239
    auto rot = Array::get_as_ref_or_tagged(header, s_key_ref_or_size_index);
72,141,411✔
240
    if (rot.is_tagged()) {
72,141,411✔
241
        return size_t(rot.get_as_int());
70,642,266✔
242
    }
70,642,266✔
243
    else {
1,499,145✔
244
        return Array::get_size_from_header(alloc.translate(rot.get_as_ref()));
1,499,145✔
245
    }
1,499,145✔
246
}
72,141,411✔
247

248
template <class T>
249
inline void Cluster::set_string_interner(T&, ColKey) const
250
{
38,608,434✔
251
}
38,608,434✔
252

253
template <>
254
inline void Cluster::set_string_interner(ArrayString& arr, ColKey col_key) const
255
{
2,999,319✔
256
    m_tree_top.set_string_interner(arr, col_key);
2,999,319✔
257
}
2,999,319✔
258

259
template <>
260
inline void Cluster::set_string_interner(ArrayMixed& arr, ColKey col_key) const
261
{
316,650✔
262
    m_tree_top.set_string_interner(arr, col_key);
316,650✔
263
}
316,650✔
264

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

270
    T arr(m_alloc);
33,948,033✔
271
    auto col_ndx = col.get_index();
33,948,033✔
272
    arr.set_parent(this, col_ndx.val + s_first_col_index);
33,948,033✔
273
    set_string_interner<T>(arr, col);
33,948,033✔
274
    arr.init_from_parent();
33,948,033✔
275
    if (init_val.is_null()) {
33,948,033✔
276
        arr.insert(ndx, T::default_value(nullable));
32,851,980✔
277
    }
32,851,980✔
278
    else {
1,096,053✔
279
        arr.insert(ndx, init_val.get<U>());
1,096,053✔
280
    }
1,096,053✔
281
}
33,948,033✔
282

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

292
    // Insert backlink if link is not null
293
    if (target_key) {
278,937✔
294
        const Table* origin_table = m_tree_top.get_owning_table();
12✔
295
        ColKey opp_col = origin_table->get_opposite_column(col_key);
12✔
296
        TableRef opp_table = origin_table->get_opposite_table(col_key);
12✔
297
        Obj target_obj = opp_table->get_object(target_key);
12✔
298
        target_obj.add_backlink(opp_col, origin_key);
12✔
299
    }
12✔
300
}
278,937✔
301

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

310
    // Insert backlink if needed
311
    if (init_value.is_type(type_TypedLink)) {
167,436✔
312
        // In case we are inserting in a Dictionary cluster, the backlink will
313
        // be handled in Dictionary::insert function
314
        if (Table* origin_table = const_cast<Table*>(m_tree_top.get_owning_table())) {
24✔
315
            if (origin_table->is_asymmetric()) {
24✔
316
                throw IllegalOperation("Object value not supported in asymmetric table");
6✔
317
            }
6✔
318
            ObjLink link = init_value.get<ObjLink>();
18✔
319
            auto target_table = origin_table->get_parent_group()->get_table(link.get_table_key());
18✔
320
            if (target_table->is_asymmetric()) {
18✔
321
                throw IllegalOperation("Ephemeral object value not supported");
6✔
322
            }
6✔
323
            ColKey backlink_col_key = target_table->find_or_add_backlink_column(col_key, origin_table->get_key());
12✔
324
            target_table->get_object(link.get_obj_key()).add_backlink(backlink_col_key, origin_key);
12✔
325
        }
12✔
326
    }
24✔
327
}
167,436✔
328

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

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

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

348
void Cluster::insert_row(size_t ndx, RowKey row_key, const FieldValues& init_values)
349
{
24,529,743✔
350
    // Ensure the cluster array is big enough to hold 64 bit values.
351
    copy_on_write(m_size * 8);
24,529,743✔
352

353
    if (m_keys.is_attached()) {
24,529,743✔
354
        m_keys.insert(ndx, row_key.value);
915,399✔
355
    }
915,399✔
356
    else {
23,614,344✔
357
        Array::set(s_key_ref_or_size_index, Array::get(s_key_ref_or_size_index) + 2); // Increments size by 1
23,614,344✔
358
    }
23,614,344✔
359

360
    auto val = init_values.begin();
24,529,743✔
361
    auto insert_in_column = [&](ColKey col_key) {
36,253,674✔
362
        auto col_ndx = col_key.get_index();
36,253,674✔
363
        auto attr = col_key.get_attrs();
36,253,674✔
364
        Mixed init_value;
36,253,674✔
365
        // init_values must be sorted in col_ndx order - this is ensured by ClustTree::insert()
366
        if (val != init_values.end() && val->col_key.get_index().val == col_ndx.val) {
36,253,674✔
367
            init_value = val->value;
982,854✔
368
            ++val;
982,854✔
369
        }
982,854✔
370

371
        auto type = col_key.get_type();
36,253,674✔
372
        if (attr.test(col_attr_Collection)) {
36,253,674✔
373
            REALM_ASSERT(init_value.is_null());
752,379✔
374
            ArrayRef arr(m_alloc);
752,379✔
375
            arr.set_parent(this, col_ndx.val + s_first_col_index);
752,379✔
376
            arr.init_from_parent();
752,379✔
377
            arr.insert(ndx, 0);
752,379✔
378
            return IteratorControl::AdvanceToNext;
752,379✔
379
        }
752,379✔
380

381
        bool nullable = attr.test(col_attr_Nullable);
35,501,295✔
382
        ObjKey obj_key(int64_t(row_key.value + get_offset()));
35,501,295✔
383
        switch (type) {
35,501,295✔
384
            case col_type_Int:
26,866,557✔
385
                if (attr.test(col_attr_Nullable)) {
26,866,557✔
386
                    do_insert_row<ArrayIntNull>(ndx, col_key, init_value, nullable);
2,869,071✔
387
                }
2,869,071✔
388
                else {
23,997,486✔
389
                    do_insert_row<ArrayInteger>(ndx, col_key, init_value, nullable);
23,997,486✔
390
                }
23,997,486✔
391
                break;
26,866,557✔
392
            case col_type_Bool:
186,603✔
393
                do_insert_row<ArrayBoolNull>(ndx, col_key, init_value, nullable);
186,603✔
394
                break;
186,603✔
395
            case col_type_Float:
331,830✔
396
                do_insert_row<ArrayFloatNull>(ndx, col_key, init_value, nullable);
331,830✔
397
                break;
331,830✔
398
            case col_type_Double:
1,493,229✔
399
                do_insert_row<ArrayDoubleNull>(ndx, col_key, init_value, nullable);
1,493,229✔
400
                break;
1,493,229✔
401
            case col_type_String:
2,429,301✔
402
                do_insert_row<ArrayString>(ndx, col_key, init_value, nullable);
2,429,301✔
403
                break;
2,429,301✔
404
            case col_type_Binary:
1,368,474✔
405
                do_insert_row<ArrayBinary>(ndx, col_key, init_value, nullable);
1,368,474✔
406
                break;
1,368,474✔
407
            case col_type_Mixed: {
167,436✔
408
                do_insert_mixed(ndx, col_key, init_value, obj_key);
167,436✔
409
                break;
167,436✔
410
            }
×
411
            case col_type_Timestamp:
825,711✔
412
                do_insert_row<ArrayTimestamp>(ndx, col_key, init_value, nullable);
825,711✔
413
                break;
825,711✔
414
            case col_type_Decimal:
78,222✔
415
                do_insert_row<ArrayDecimal128>(ndx, col_key, init_value, nullable);
78,222✔
416
                break;
78,222✔
417
            case col_type_ObjectId:
240,246✔
418
                do_insert_row<ArrayObjectIdNull>(ndx, col_key, init_value, nullable);
240,246✔
419
                break;
240,246✔
420
            case col_type_UUID:
119,652✔
421
                do_insert_row<ArrayUUIDNull>(ndx, col_key, init_value, nullable);
119,652✔
422
                break;
119,652✔
423
            case col_type_Link:
278,937✔
424
                do_insert_key(ndx, col_key, init_value, obj_key);
278,937✔
425
                break;
278,937✔
426
            case col_type_TypedLink:
✔
427
                do_insert_link(ndx, col_key, init_value, obj_key);
×
428
                break;
×
429
            case col_type_BackLink: {
1,154,205✔
430
                ArrayBacklink arr(m_alloc);
1,154,205✔
431
                arr.set_parent(this, col_ndx.val + s_first_col_index);
1,154,205✔
432
                arr.init_from_parent();
1,154,205✔
433
                arr.insert(ndx, 0);
1,154,205✔
434
                break;
1,154,205✔
435
            }
×
436
            default:
✔
437
                REALM_ASSERT(false);
×
438
                break;
×
439
        }
35,501,295✔
440
        return IteratorControl::AdvanceToNext;
35,392,203✔
441
    };
35,501,295✔
442
    m_tree_top.m_owner->for_each_and_every_column(insert_in_column);
24,529,743✔
443
}
24,529,743✔
444

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

454
    T dst(m_alloc);
23,958✔
455
    dst.set_parent(to, col_ndx);
23,958✔
456
    dst.init_from_parent();
23,958✔
457
    set_string_interner<T>(dst, col_key);
23,958✔
458

459
    src.move(dst, ndx);
23,958✔
460
}
23,958✔
461

462
void Cluster::move(size_t ndx, ClusterNode* new_node, int64_t offset)
463
{
12,546✔
464
    auto new_leaf = static_cast<Cluster*>(new_node);
12,546✔
465

466
    auto move_from_column = [&](ColKey col_key) {
23,958✔
467
        auto attr = col_key.get_attrs();
23,958✔
468
        auto type = col_key.get_type();
23,958✔
469

470
        if (attr.test(col_attr_Collection)) {
23,958✔
471
            do_move<ArrayRef>(ndx, col_key, new_leaf);
294✔
472
            return IteratorControl::AdvanceToNext;
294✔
473
        }
294✔
474

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

536
Cluster::~Cluster() {}
334,715,688✔
537

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

546
void Cluster::ensure_general_form()
547
{
61,278✔
548
    if (!m_keys.is_attached()) {
61,278✔
549
        size_t current_size = get_size_in_compact_form();
45,939✔
550
        m_keys.create(current_size, 255);
45,939✔
551
        m_keys.update_parent();
45,939✔
552
        for (size_t i = 0; i < current_size; i++) {
5,300,880✔
553
            m_keys.set(i, i);
5,254,941✔
554
        }
5,254,941✔
555
    }
45,939✔
556
}
61,278✔
557

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

563
    T arr(m_alloc);
623,343✔
564
    arr.create();
623,343✔
565
    auto val = T::default_value(nullable);
623,343✔
566
    for (size_t i = 0; i < sz; i++) {
6,861,918✔
567
        arr.add(val);
6,238,575✔
568
    }
6,238,575✔
569
    auto col_ndx = col_key.get_index();
623,343✔
570
    unsigned ndx = col_ndx.val + s_first_col_index;
623,343✔
571

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

576
    if (ndx == size())
623,343✔
577
        Array::insert(ndx, from_ref(arr.get_ref()));
623,241✔
578
    else
102✔
579
        Array::set(ndx, from_ref(arr.get_ref()));
102✔
580
}
623,343✔
581

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

589
        ArrayRef arr(m_alloc);
195,795✔
590
        arr.create(sz);
195,795✔
591
        auto col_ndx = col_key.get_index();
195,795✔
592
        unsigned idx = col_ndx.val + s_first_col_index;
195,795✔
593
        if (idx == size())
195,795✔
594
            Array::insert(idx, from_ref(arr.get_ref()));
195,795✔
595
        else
×
596
            Array::set(idx, from_ref(arr.get_ref()));
×
597
        return;
195,795✔
598
    }
195,795✔
599
    bool nullable = attr.test(col_attr_Nullable);
623,340✔
600
    switch (type) {
623,340✔
601
        case col_type_Int:
280,836✔
602
            if (nullable) {
280,836✔
603
                do_insert_column<ArrayIntNull>(col_key, nullable);
32,946✔
604
            }
32,946✔
605
            else {
247,890✔
606
                do_insert_column<ArrayInteger>(col_key, nullable);
247,890✔
607
            }
247,890✔
608
            break;
280,836✔
609
        case col_type_Bool:
5,475✔
610
            do_insert_column<ArrayBoolNull>(col_key, nullable);
5,475✔
611
            break;
5,475✔
612
        case col_type_Float:
6,444✔
613
            do_insert_column<ArrayFloatNull>(col_key, nullable);
6,444✔
614
            break;
6,444✔
615
        case col_type_Double:
7,083✔
616
            do_insert_column<ArrayDoubleNull>(col_key, nullable);
7,083✔
617
            break;
7,083✔
618
        case col_type_String:
104,235✔
619
            do_insert_column<ArrayString>(col_key, nullable);
104,235✔
620
            break;
104,235✔
621
        case col_type_Binary:
7,254✔
622
            do_insert_column<ArrayBinary>(col_key, nullable);
7,254✔
623
            break;
7,254✔
624
        case col_type_Mixed:
9,186✔
625
            do_insert_column<ArrayMixed>(col_key, nullable);
9,186✔
626
            break;
9,186✔
627
        case col_type_Timestamp:
24,441✔
628
            do_insert_column<ArrayTimestamp>(col_key, nullable);
24,441✔
629
            break;
24,441✔
630
        case col_type_Decimal:
5,022✔
631
            do_insert_column<ArrayDecimal128>(col_key, nullable);
5,022✔
632
            break;
5,022✔
633
        case col_type_ObjectId:
51,507✔
634
            do_insert_column<ArrayObjectIdNull>(col_key, nullable);
51,507✔
635
            break;
51,507✔
636
        case col_type_UUID:
5,616✔
637
            do_insert_column<ArrayUUIDNull>(col_key, nullable);
5,616✔
638
            break;
5,616✔
639
        case col_type_Link:
31,404✔
640
            do_insert_column<ArrayKey>(col_key, nullable);
31,404✔
641
            break;
31,404✔
642
        case col_type_TypedLink:
✔
643
            do_insert_column<ArrayTypedLink>(col_key, nullable);
×
644
            break;
×
645
        case col_type_BackLink:
84,843✔
646
            do_insert_column<ArrayBacklink>(col_key, nullable);
84,843✔
647
            break;
84,843✔
648
        default:
✔
649
            REALM_UNREACHABLE();
650
            break;
×
651
    }
623,340✔
652
}
623,340✔
653

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

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

675
    auto on_error = [&] {
24,562,224✔
676
        throw KeyAlreadyUsed(
18✔
677
            util::format("When inserting key '%1' in '%2'", row_key.value, get_owning_table()->get_name()));
18✔
678
    };
18✔
679

680
    if (m_keys.is_attached()) {
24,562,224✔
681
        sz = m_keys.size();
903,753✔
682
        ndx = m_keys.lower_bound(row_key.value);
903,753✔
683
        if (ndx < sz) {
903,753✔
684
            current_key_value = m_keys.get(ndx);
612,969✔
685
            if (row_key.value == uint64_t(current_key_value)) {
612,969✔
686
                on_error();
18✔
687
            }
18✔
688
        }
612,969✔
689
    }
903,753✔
690
    else {
23,658,471✔
691
        sz = size_t(Array::get(s_key_ref_or_size_index)) >> 1; // Size is stored as tagged integer
23,658,471✔
692
        if (row_key.value < sz) {
23,658,471✔
693
            on_error();
×
694
        }
×
695
        // Key value is bigger than all other values, should be put last
696
        ndx = sz;
23,658,471✔
697
        if (row_key.value > sz && sz < cluster_node_size) {
23,658,471✔
698
            ensure_general_form();
12,966✔
699
        }
12,966✔
700
    }
23,658,471✔
701

702
    REALM_ASSERT_DEBUG(sz <= cluster_node_size);
24,562,224✔
703
    if (REALM_LIKELY(sz < cluster_node_size)) {
24,562,224✔
704
        insert_row(ndx, row_key, init_values); // Throws
24,426,516✔
705
        state.mem = get_mem();
24,426,516✔
706
        state.index = ndx;
24,426,516✔
707
    }
24,426,516✔
708
    else {
135,708✔
709
        // Split leaf node
710
        Cluster new_leaf(0, m_alloc, m_tree_top);
135,708✔
711
        new_leaf.create();
135,708✔
712
        if (ndx == sz) {
137,547✔
713
            new_leaf.insert_row(0, RowKey(0), init_values); // Throws
89,985✔
714
            state.split_key = int64_t(row_key.value);
89,985✔
715
            state.mem = new_leaf.get_mem();
89,985✔
716
            state.index = 0;
89,985✔
717
        }
89,985✔
718
        else {
2,147,531,209✔
719
            // Current cluster must be in general form to get here
720
            REALM_ASSERT_DEBUG(m_keys.is_attached());
2,147,531,209✔
721
            new_leaf.ensure_general_form();
2,147,531,209✔
722
            move(ndx, &new_leaf, current_key_value);
2,147,531,209✔
723
            insert_row(ndx, row_key, init_values); // Throws
2,147,531,209✔
724
            state.mem = get_mem();
2,147,531,209✔
725
            state.split_key = current_key_value;
2,147,531,209✔
726
            state.index = ndx;
2,147,531,209✔
727
        }
2,147,531,209✔
728
        ret = new_leaf.get_ref();
135,708✔
729
    }
135,708✔
730

731
    return ret;
24,562,224✔
732
}
24,562,224✔
733

734
bool Cluster::try_get(RowKey k, ClusterNode::State& state) const noexcept
735
{
282,149,670✔
736
    state.mem = get_mem();
282,149,670✔
737
    if (m_keys.is_attached()) {
282,149,670✔
738
        state.index = m_keys.lower_bound(k.value);
50,055,384✔
739
        return state.index != m_keys.size() && m_keys.get(state.index) == k.value;
50,055,384✔
740
    }
50,055,384✔
741
    else {
232,094,286✔
742
        if (k.value < uint64_t(Array::get(s_key_ref_or_size_index) >> 1)) {
232,094,286✔
743
            state.index = size_t(k.value);
213,754,617✔
744
            return true;
213,754,617✔
745
        }
213,754,617✔
746
    }
232,094,286✔
747
    return false;
18,339,669✔
748
}
282,149,670✔
749

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

757
template <class T>
758
inline void Cluster::do_erase(size_t ndx, ColKey col_key)
759
{
7,095,162✔
760
    auto col_ndx = col_key.get_index();
7,095,162✔
761
    T values(m_alloc);
7,095,162✔
762
    values.set_parent(this, col_ndx.val + s_first_col_index);
7,095,162✔
763
    set_string_interner<T>(values, col_key);
7,095,162✔
764
    values.init_from_parent();
7,095,162✔
765
    if constexpr (std::is_same_v<T, ArrayTypedLink>) {
7,095,162✔
766
        if (ObjLink link = values.get(ndx)) {
×
767
            if (const Table* origin_table = m_tree_top.get_owning_table()) {
×
768
                auto target_obj = origin_table->get_parent_group()->get_object(link);
×
769

770
                ColKey backlink_col_key =
×
771
                    target_obj.get_table()->find_backlink_column(col_key, origin_table->get_key());
×
772
                REALM_ASSERT(backlink_col_key);
×
773
                target_obj.remove_one_backlink(backlink_col_key, get_real_key(ndx)); // Throws
×
774
            }
×
775
        }
×
776
    }
×
777
    values.erase(ndx);
7,095,162✔
778
}
7,095,162✔
779

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

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

790
    Mixed value = values.get(ndx);
145,431✔
791
    if (value.is_type(type_TypedLink)) {
145,431✔
792
        ObjLink link = value.get<ObjLink>();
30✔
793
        Obj obj(origin_table->m_own_ref, get_mem(), get_real_key(ndx), ndx);
30✔
794
        obj.remove_backlink(col_key, link, state);
30✔
795
    }
30✔
796
    if (value.is_type(type_List)) {
145,431✔
797
        Obj obj(origin_table->m_own_ref, get_mem(), get_real_key(ndx), ndx);
72,042✔
798
        Lst<Mixed> list(obj, col_key);
72,042✔
799
        list.remove_backlinks(state);
72,042✔
800
    }
72,042✔
801
    if (value.is_type(type_Dictionary)) {
145,431✔
802
        Obj obj(origin_table->m_own_ref, get_mem(), get_real_key(ndx), ndx);
72,015✔
803
        Dictionary dict(obj, col_key);
72,015✔
804
        dict.remove_backlinks(state);
72,015✔
805
    }
72,015✔
806
    values.erase(ndx);
145,431✔
807
}
145,431✔
808

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

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

823
size_t Cluster::get_ndx(RowKey k, size_t ndx) const noexcept
824
{
10,544,619✔
825
    size_t index;
10,544,619✔
826
    if (m_keys.is_attached()) {
10,544,619✔
827
        index = m_keys.lower_bound(k.value);
10,425,828✔
828
        if (index == m_keys.size() || m_keys.get(index) != k.value) {
10,427,046✔
829
            return realm::npos;
18✔
830
        }
18✔
831
    }
10,425,828✔
832
    else {
118,791✔
833
        index = size_t(k.value);
118,791✔
834
        if (index >= get_as_ref_or_tagged(s_key_ref_or_size_index).get_as_int()) {
118,791✔
835
            return realm::npos;
×
836
        }
×
837
    }
118,791✔
838
    return index + ndx;
10,544,601✔
839
}
10,544,619✔
840

841
size_t Cluster::erase(RowKey row_key, CascadeState& state)
842
{
5,316,906✔
843
    size_t ndx = get_ndx(row_key, 0);
5,316,906✔
844
    if (ndx == realm::npos)
5,316,906✔
845
        throw KeyNotFound(util::format("When erasing key '%1' (offset '%2') in '%3'", row_key.value, m_offset,
18✔
846
                                       get_owning_table()->get_name()));
18✔
847

848
    ObjKey real_key = get_real_key(ndx);
5,316,888✔
849
    std::vector<ColKey> backlink_column_keys;
5,316,888✔
850

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

861
            if (ref) {
104,688✔
862
                const Table* origin_table = m_tree_top.get_owning_table();
84,132✔
863
                if (attr.test(col_attr_Dictionary)) {
84,132✔
864
                    if (col_type == col_type_Mixed || col_type == col_type_Link) {
38,655✔
865
                        Obj obj(origin_table->m_own_ref, get_mem(), real_key, ndx);
36,354✔
866
                        Dictionary dict(obj, col_key);
36,354✔
867
                        dict.remove_backlinks(state);
36,354✔
868
                    }
36,354✔
869
                }
38,655✔
870
                else if (col_type == col_type_Link) {
45,477✔
871
                    BPlusTree<ObjKey> links(m_alloc);
4,677✔
872
                    links.init_from_ref(ref);
4,677✔
873
                    if (links.size() > 0) {
4,677✔
874
                        do_remove_backlinks(real_key, col_key, links.get_all(), state);
1,041✔
875
                    }
1,041✔
876
                }
4,677✔
877
                else if (col_type == col_type_TypedLink) {
40,800✔
878
                    BPlusTree<ObjLink> links(m_alloc);
×
879
                    links.init_from_ref(ref);
×
880
                    for (size_t i = 0; i < links.size(); i++) {
×
881
                        ObjLink link = links.get(i);
×
882
                        auto target_obj = origin_table->get_parent_group()->get_object(link);
×
883
                        ColKey backlink_col_key =
×
884
                            target_obj.get_table()->find_backlink_column(col_key, origin_table->get_key());
×
885
                        target_obj.remove_one_backlink(backlink_col_key, real_key);
×
886
                    }
×
887
                }
×
888
                else if (col_type == col_type_Mixed) {
40,800✔
889
                    Obj obj(origin_table->m_own_ref, get_mem(), real_key, ndx);
36,246✔
890
                    Lst<Mixed> list(obj, col_key);
36,246✔
891
                    list.remove_backlinks(state);
36,246✔
892
                }
36,246✔
893
                Array::destroy_deep(ref, m_alloc);
84,132✔
894
            }
84,132✔
895

896
            values.erase(ndx);
104,688✔
897

898
            return IteratorControl::AdvanceToNext;
104,688✔
899
        }
104,688✔
900

901
        switch (col_type) {
7,333,242✔
902
            case col_type_Int:
6,043,635✔
903
                if (attr.test(col_attr_Nullable)) {
6,043,635✔
904
                    do_erase<ArrayIntNull>(ndx, col_key);
1,275,828✔
905
                }
1,275,828✔
906
                else {
4,767,807✔
907
                    do_erase<ArrayInteger>(ndx, col_key);
4,767,807✔
908
                }
4,767,807✔
909
                break;
6,043,635✔
910
            case col_type_Bool:
42,435✔
911
                do_erase<ArrayBoolNull>(ndx, col_key);
42,435✔
912
                break;
42,435✔
913
            case col_type_Float:
36,492✔
914
                do_erase<ArrayFloatNull>(ndx, col_key);
36,492✔
915
                break;
36,492✔
916
            case col_type_Double:
36,501✔
917
                do_erase<ArrayDoubleNull>(ndx, col_key);
36,501✔
918
                break;
36,501✔
919
            case col_type_String:
422,229✔
920
                do_erase<ArrayString>(ndx, col_key);
422,229✔
921
                break;
422,229✔
922
            case col_type_Binary:
43,581✔
923
                do_erase<ArrayBinary>(ndx, col_key);
43,581✔
924
                break;
43,581✔
925
            case col_type_Mixed:
145,428✔
926
                do_erase_mixed(ndx, col_key, state);
145,428✔
927
                break;
145,428✔
928
            case col_type_Timestamp:
45,381✔
929
                do_erase<ArrayTimestamp>(ndx, col_key);
45,381✔
930
                break;
45,381✔
931
            case col_type_Decimal:
36,279✔
932
                do_erase<ArrayDecimal128>(ndx, col_key);
36,279✔
933
                break;
36,279✔
934
            case col_type_ObjectId:
73,959✔
935
                do_erase<ArrayObjectIdNull>(ndx, col_key);
73,959✔
936
                break;
73,959✔
937
            case col_type_UUID:
60,285✔
938
                do_erase<ArrayUUIDNull>(ndx, col_key);
60,285✔
939
                break;
60,285✔
940
            case col_type_Link:
92,832✔
941
                do_erase_key(ndx, col_key, state);
92,832✔
942
                break;
92,832✔
943
            case col_type_TypedLink:
✔
944
                do_erase<ArrayTypedLink>(ndx, col_key);
×
945
                break;
×
946
            case col_type_BackLink:
254,388✔
947
                if (state.m_mode == CascadeState::Mode::None) {
254,388✔
948
                    do_erase<ArrayBacklink>(ndx, col_key);
188,610✔
949
                }
188,610✔
950
                else {
65,778✔
951
                    // Postpone the deletion of backlink entries or else the
952
                    // checks for if there's any remaining backlinks will
953
                    // check the wrong row for columns which have already
954
                    // had values erased from them.
955
                    backlink_column_keys.push_back(col_key);
65,778✔
956
                }
65,778✔
957
                break;
254,388✔
958
            default:
✔
959
                REALM_ASSERT(false);
×
960
                break;
×
961
        }
7,333,242✔
962
        return IteratorControl::AdvanceToNext;
7,333,065✔
963
    };
7,333,242✔
964
    m_tree_top.m_owner->for_each_and_every_column(erase_in_column);
5,316,888✔
965

966
    // Any remaining backlink columns to erase from?
967
    for (auto k : backlink_column_keys)
5,316,888✔
968
        do_erase<ArrayBacklink>(ndx, k);
65,781✔
969

970
    if (m_keys.is_attached()) {
5,316,888✔
971
        m_keys.erase(ndx);
5,274,174✔
972
    }
5,274,174✔
973
    else {
42,714✔
974
        size_t current_size = get_size_in_compact_form();
42,714✔
975
        if (ndx == current_size - 1) {
42,714✔
976
            // When deleting last, we can still maintain compact form
977
            set(0, RefOrTagged::make_tagged(current_size - 1));
27,048✔
978
        }
27,048✔
979
        else {
15,666✔
980
            ensure_general_form();
15,666✔
981
            m_keys.erase(ndx);
15,666✔
982
        }
15,666✔
983
    }
42,714✔
984

985
    return node_size();
5,316,888✔
986
}
5,316,906✔
987

988
void Cluster::nullify_incoming_links(RowKey key, CascadeState& state)
989
{
5,191,791✔
990
    size_t ndx = get_ndx(key, 0);
5,191,791✔
991
    if (ndx == realm::npos)
5,191,791✔
992
        throw KeyNotFound(util::format("Key '%1' not found in '%2' when nullifying incoming links", key.value,
×
993
                                       get_owning_table()->get_class_name()));
×
994

995
    // We must start with backlink columns in case the corresponding link
996
    // columns are in the same table so that we can nullify links before
997
    // erasing rows in the link columns.
998
    //
999
    // This phase also generates replication instructions documenting the side-
1000
    // effects of deleting the object (i.e. link nullifications). These instructions
1001
    // must come before the actual deletion of the object, but at the same time
1002
    // the Replication object may need a consistent view of the row (not including
1003
    // link columns). Therefore we first nullify links to this object, then
1004
    // generate the instruction, and then delete the row in the remaining columns.
1005

1006
    auto nullify_fwd_links = [&](ColKey col_key) {
5,191,791✔
1007
        ColKey::Idx leaf_ndx = col_key.get_index();
233,115✔
1008
        auto type = col_key.get_type();
233,115✔
1009
        REALM_ASSERT(type == col_type_BackLink);
233,115✔
1010
        ArrayBacklink values(m_alloc);
233,115✔
1011
        values.set_parent(this, leaf_ndx.val + s_first_col_index);
233,115✔
1012
        values.init_from_parent();
233,115✔
1013
        // Ensure that Cluster is writable and able to hold references to nodes in
1014
        // the slab area before nullifying or deleting links. These operation may
1015
        // both have the effect that other objects may be constructed and manipulated.
1016
        // If those other object are in the same cluster that the object to be deleted
1017
        // is in, then that will cause another accessor to this cluster to be created.
1018
        // It would lead to an error if the cluster node was relocated without it being
1019
        // reflected in the context here.
1020
        values.copy_on_write();
233,115✔
1021
        values.nullify_fwd_links(ndx, state);
233,115✔
1022

1023
        return IteratorControl::AdvanceToNext;
233,115✔
1024
    };
233,115✔
1025

1026
    m_tree_top.get_owning_table()->for_each_backlink_column(nullify_fwd_links);
5,191,791✔
1027
}
5,191,791✔
1028

1029
void Cluster::init_leaf(ColKey col_key, ArrayPayload* leaf) const
1030
{
8,707,512✔
1031
    auto col_ndx = col_key.get_index();
8,707,512✔
1032
    // FIXME: Move this validation into callers.
1033
    // Currently, the query subsystem may call with an unvalidated key.
1034
    // once fixed, reintroduce the noexcept declaration :-D
1035
    if (auto t = m_tree_top.get_owning_table())
8,707,512✔
1036
        t->check_column(col_key);
8,794,269✔
1037
    ref_type ref = to_ref(Array::get(col_ndx.val + 1));
8,707,512✔
1038
    if (leaf->need_string_interner()) {
8,707,512✔
1039
        m_tree_top.set_string_interner(*leaf, col_key);
163,719✔
1040
    }
163,719✔
1041
    leaf->init_from_ref(ref);
8,707,512✔
1042
    leaf->set_parent(const_cast<Cluster*>(this), col_ndx.val + 1);
8,707,512✔
1043
}
8,707,512✔
1044

1045
void Cluster::add_leaf(ColKey col_key, ref_type ref)
1046
{
×
1047
    auto col_ndx = col_key.get_index();
×
1048
    REALM_ASSERT((col_ndx.val + 1) == size());
×
1049
    Array::insert(col_ndx.val + 1, from_ref(ref));
×
1050
}
×
1051

1052
template <typename ArrayType>
1053
void Cluster::verify(ref_type ref, size_t index, util::Optional<size_t>& sz) const
1054
{
527,709✔
1055
    ArrayType arr(get_alloc());
527,709✔
1056
    auto table = get_owning_table();
527,709✔
1057
    REALM_ASSERT(index <= table->m_leaf_ndx2colkey.size());
527,709✔
1058
    auto col_key = table->m_leaf_ndx2colkey[index - 1];
527,709✔
1059
    set_string_interner(arr, col_key);
527,709✔
1060
    arr.set_parent(const_cast<Cluster*>(this), index);
527,709✔
1061
    arr.init_from_ref(ref);
527,709✔
1062
    arr.verify();
527,709✔
1063
    if (sz) {
527,709✔
1064
        REALM_ASSERT(arr.size() == *sz);
191,898✔
1065
    }
191,898✔
1066
    else {
335,811✔
1067
        sz = arr.size();
335,811✔
1068
    }
335,811✔
1069
}
527,709✔
1070
namespace {
1071

1072
template <typename ArrayType>
1073
void verify_list(ArrayRef& arr, size_t sz)
1074
{
81,651✔
1075
    for (size_t n = 0; n < sz; n++) {
297,435!
1076
        if (ref_type bp_tree_ref = arr.get(n)) {
215,784!
1077
            BPlusTree<ArrayType> links(arr.get_alloc());
101,931✔
1078
            links.init_from_ref(bp_tree_ref);
101,931✔
1079
            links.set_parent(&arr, n);
101,931✔
1080
            links.verify();
101,931✔
1081
        }
101,931✔
1082
    }
215,784✔
1083
}
81,651✔
1084

1085
template <typename SetType>
1086
void verify_set(ArrayRef& arr, size_t sz)
1087
{
492✔
1088
    for (size_t n = 0; n < sz; ++n) {
1,038!
1089
        if (ref_type bp_tree_ref = arr.get(n)) {
546!
1090
            BPlusTree<SetType> elements(arr.get_alloc());
528✔
1091
            elements.init_from_ref(bp_tree_ref);
528✔
1092
            elements.set_parent(&arr, n);
528✔
1093
            elements.verify();
528✔
1094

1095
            // FIXME: Check uniqueness of elements.
1096
        }
528✔
1097
    }
546✔
1098
}
492✔
1099

1100
} // namespace
1101

1102
void Cluster::verify() const
1103
{
372,570✔
1104
#ifdef REALM_DEBUG
372,570✔
1105
    util::Optional<size_t> sz;
372,570✔
1106

1107
    auto verify_column = [this, &sz](ColKey col_key) {
622,434✔
1108
        size_t col = col_key.get_index().val + s_first_col_index;
622,434✔
1109
        ref_type ref = Array::get_as_ref(col);
622,434✔
1110
        auto attr = col_key.get_attrs();
622,434✔
1111
        auto col_type = col_key.get_type();
622,434✔
1112
        bool nullable = attr.test(col_attr_Nullable);
622,434✔
1113

1114
        if (attr.test(col_attr_List)) {
622,434✔
1115
            ArrayRef arr(get_alloc());
81,669✔
1116
            arr.set_parent(const_cast<Cluster*>(this), col);
81,669✔
1117
            arr.init_from_ref(ref);
81,669✔
1118
            arr.verify();
81,669✔
1119
            if (sz) {
81,669✔
1120
                REALM_ASSERT(arr.size() == *sz);
57,369✔
1121
            }
57,369✔
1122
            else {
24,300✔
1123
                sz = arr.size();
24,300✔
1124
            }
24,300✔
1125

1126
            switch (col_type) {
81,669✔
1127
                case col_type_Int:
34,857✔
1128
                    if (nullable) {
34,857✔
1129
                        verify_list<util::Optional<int64_t>>(arr, *sz);
×
1130
                    }
×
1131
                    else {
34,857✔
1132
                        verify_list<int64_t>(arr, *sz);
34,857✔
1133
                    }
34,857✔
1134
                    break;
34,857✔
1135
                case col_type_Bool:
✔
1136
                    verify_list<Bool>(arr, *sz);
×
1137
                    break;
×
1138
                case col_type_Float:
6✔
1139
                    verify_list<Float>(arr, *sz);
6✔
1140
                    break;
6✔
1141
                case col_type_Double:
✔
1142
                    verify_list<Double>(arr, *sz);
×
1143
                    break;
×
1144
                case col_type_String:
21,702✔
1145
                    verify_list<String>(arr, *sz);
21,702✔
1146
                    break;
21,702✔
1147
                case col_type_Binary:
24,000✔
1148
                    verify_list<Binary>(arr, *sz);
24,000✔
1149
                    break;
24,000✔
1150
                case col_type_Timestamp:
✔
1151
                    verify_list<Timestamp>(arr, *sz);
×
1152
                    break;
×
1153
                case col_type_Decimal:
✔
1154
                    verify_list<Decimal128>(arr, *sz);
×
1155
                    break;
×
1156
                case col_type_ObjectId:
✔
1157
                    verify_list<ObjectId>(arr, *sz);
×
1158
                    break;
×
1159
                case col_type_UUID:
✔
1160
                    verify_list<UUID>(arr, *sz);
×
1161
                    break;
×
1162
                case col_type_Link:
1,086✔
1163
                    verify_list<ObjKey>(arr, *sz);
1,086✔
1164
                    break;
1,086✔
1165
                default:
18✔
1166
                    // FIXME: Nullable primitives
1167
                    break;
18✔
1168
            }
81,669✔
1169
            return IteratorControl::AdvanceToNext;
81,669✔
1170
        }
81,669✔
1171
        else if (attr.test(col_attr_Dictionary)) {
540,765✔
1172
            ArrayRef arr(get_alloc());
12,486✔
1173
            arr.set_parent(const_cast<Cluster*>(this), col);
12,486✔
1174
            arr.init_from_ref(ref);
12,486✔
1175
            arr.verify();
12,486✔
1176
            if (sz) {
12,486✔
1177
                REALM_ASSERT(arr.size() == *sz);
456✔
1178
            }
456✔
1179
            else {
12,030✔
1180
                sz = arr.size();
12,030✔
1181
            }
12,030✔
1182
            for (size_t n = 0; n < sz; n++) {
25,140✔
1183
                if (auto ref = arr.get(n)) {
12,654✔
1184
                    Dictionary dict(get_alloc(), col_key, to_ref(ref));
12,522✔
1185
                    dict.verify();
12,522✔
1186
                }
12,522✔
1187
            }
12,654✔
1188
            return IteratorControl::AdvanceToNext;
12,486✔
1189
        }
12,486✔
1190
        else if (attr.test(col_attr_Set)) {
528,279✔
1191
            ArrayRef arr(get_alloc());
588✔
1192
            arr.set_parent(const_cast<Cluster*>(this), col);
588✔
1193
            arr.init_from_ref(ref);
588✔
1194
            arr.verify();
588✔
1195
            if (sz) {
588✔
1196
                REALM_ASSERT(arr.size() == *sz);
570✔
1197
            }
570✔
1198
            else {
18✔
1199
                sz = arr.size();
18✔
1200
            }
18✔
1201
            switch (col_type) {
588✔
1202
                case col_type_Int:
138✔
1203
                    if (nullable) {
138✔
1204
                        verify_set<util::Optional<int64_t>>(arr, *sz);
×
1205
                    }
×
1206
                    else {
138✔
1207
                        verify_set<int64_t>(arr, *sz);
138✔
1208
                    }
138✔
1209
                    break;
138✔
1210
                case col_type_Bool:
✔
1211
                    verify_set<Bool>(arr, *sz);
×
1212
                    break;
×
1213
                case col_type_Float:
✔
1214
                    verify_set<Float>(arr, *sz);
×
1215
                    break;
×
1216
                case col_type_Double:
✔
1217
                    verify_set<Double>(arr, *sz);
×
1218
                    break;
×
1219
                case col_type_String:
324✔
1220
                    verify_set<String>(arr, *sz);
324✔
1221
                    break;
324✔
1222
                case col_type_Binary:
18✔
1223
                    verify_set<Binary>(arr, *sz);
18✔
1224
                    break;
18✔
1225
                case col_type_Timestamp:
✔
1226
                    verify_set<Timestamp>(arr, *sz);
×
1227
                    break;
×
1228
                case col_type_Decimal:
✔
1229
                    verify_set<Decimal128>(arr, *sz);
×
1230
                    break;
×
1231
                case col_type_ObjectId:
✔
1232
                    verify_set<ObjectId>(arr, *sz);
×
1233
                    break;
×
1234
                case col_type_UUID:
✔
1235
                    verify_set<UUID>(arr, *sz);
×
1236
                    break;
×
1237
                case col_type_Link:
12✔
1238
                    verify_set<ObjKey>(arr, *sz);
12✔
1239
                    break;
12✔
1240
                default:
96✔
1241
                    // FIXME: Nullable primitives
1242
                    break;
96✔
1243
            }
588✔
1244
            return IteratorControl::AdvanceToNext;
588✔
1245
        }
588✔
1246

1247
        switch (col_type) {
527,691✔
1248
            case col_type_Int:
310,875✔
1249
                if (nullable) {
310,875✔
1250
                    verify<ArrayIntNull>(ref, col, sz);
24,027✔
1251
                }
24,027✔
1252
                else {
286,848✔
1253
                    verify<ArrayInteger>(ref, col, sz);
286,848✔
1254
                }
286,848✔
1255
                break;
310,875✔
1256
            case col_type_Bool:
7,140✔
1257
                verify<ArrayBoolNull>(ref, col, sz);
7,140✔
1258
                break;
7,140✔
1259
            case col_type_Float:
195✔
1260
                verify<ArrayFloatNull>(ref, col, sz);
195✔
1261
                break;
195✔
1262
            case col_type_Double:
156✔
1263
                verify<ArrayDoubleNull>(ref, col, sz);
156✔
1264
                break;
156✔
1265
            case col_type_String:
145,722✔
1266
                verify<ArrayString>(ref, col, sz);
145,722✔
1267
                break;
145,722✔
1268
            case col_type_Binary:
49,872✔
1269
                verify<ArrayBinary>(ref, col, sz);
49,872✔
1270
                break;
49,872✔
1271
            case col_type_Mixed:
2,724✔
1272
                verify<ArrayMixed>(ref, col, sz);
2,724✔
1273
                break;
2,724✔
1274
            case col_type_Timestamp:
6,948✔
1275
                verify<ArrayTimestamp>(ref, col, sz);
6,948✔
1276
                break;
6,948✔
1277
            case col_type_Decimal:
42✔
1278
                verify<ArrayDecimal128>(ref, col, sz);
42✔
1279
                break;
42✔
1280
            case col_type_ObjectId:
42✔
1281
                verify<ArrayObjectIdNull>(ref, col, sz);
42✔
1282
                break;
42✔
1283
            case col_type_UUID:
✔
1284
                verify<ArrayUUIDNull>(ref, col, sz);
×
1285
                break;
×
1286
            case col_type_Link:
1,542✔
1287
                verify<ArrayKey>(ref, col, sz);
1,542✔
1288
                break;
1,542✔
1289
            case col_type_BackLink:
2,454✔
1290
                verify<ArrayBacklink>(ref, col, sz);
2,454✔
1291
                break;
2,454✔
1292
            default:
✔
1293
                break;
×
1294
        }
527,691✔
1295
        return IteratorControl::AdvanceToNext;
527,706✔
1296
    };
527,691✔
1297

1298
    m_tree_top.m_owner->for_each_and_every_column(verify_column);
372,570✔
1299
#endif
372,570✔
1300
}
372,570✔
1301

1302
// LCOV_EXCL_START
1303
void Cluster::dump_objects(int64_t key_offset, std::string lead) const
1304
{
×
1305
    std::cout << lead << "leaf - size: " << node_size() << std::endl;
×
1306
    if (!m_keys.is_attached()) {
×
1307
        std::cout << lead << "compact form" << std::endl;
×
1308
    }
×
1309

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

1344
            switch (col.get_type()) {
×
1345
                case col_type_Int: {
×
1346
                    bool nullable = col.get_attrs().test(col_attr_Nullable);
×
1347
                    ref_type ref = Array::get_as_ref(j);
×
1348
                    if (nullable) {
×
1349
                        ArrayIntNull arr_int_null(m_alloc);
×
1350
                        arr_int_null.init_from_ref(ref);
×
1351
                        if (arr_int_null.is_null(i)) {
×
1352
                            std::cout << ", null";
×
1353
                        }
×
1354
                        else {
×
1355
                            std::cout << ", " << *arr_int_null.get(i);
×
1356
                        }
×
1357
                    }
×
1358
                    else {
×
1359
                        Array arr(m_alloc);
×
1360
                        arr.init_from_ref(ref);
×
1361
                        std::cout << ", " << arr.get(i);
×
1362
                    }
×
1363
                    break;
×
1364
                }
×
1365
                case col_type_Bool: {
×
1366
                    ArrayBoolNull arr(m_alloc);
×
1367
                    ref_type ref = Array::get_as_ref(j);
×
1368
                    arr.init_from_ref(ref);
×
1369
                    auto val = arr.get(i);
×
1370
                    std::cout << ", " << (val ? (*val ? "true" : "false") : "null");
×
1371
                    break;
×
1372
                }
×
1373
                case col_type_Float: {
×
1374
                    ArrayFloatNull arr(m_alloc);
×
1375
                    ref_type ref = Array::get_as_ref(j);
×
1376
                    arr.init_from_ref(ref);
×
1377
                    auto val = arr.get(i);
×
1378
                    if (val)
×
1379
                        std::cout << ", " << *val;
×
1380
                    else
×
1381
                        std::cout << ", null";
×
1382
                    break;
×
1383
                }
×
1384
                case col_type_Double: {
×
1385
                    ArrayDoubleNull arr(m_alloc);
×
1386
                    ref_type ref = Array::get_as_ref(j);
×
1387
                    arr.init_from_ref(ref);
×
1388
                    auto val = arr.get(i);
×
1389
                    if (val)
×
1390
                        std::cout << ", " << *val;
×
1391
                    else
×
1392
                        std::cout << ", null";
×
1393
                    break;
×
1394
                }
×
1395
                case col_type_String: {
×
1396
                    ArrayString arr(m_alloc);
×
NEW
1397
                    set_string_interner(arr, col);
×
1398
                    ref_type ref = Array::get_as_ref(j);
×
1399
                    arr.init_from_ref(ref);
×
1400
                    std::cout << ", " << arr.get(i);
×
1401
                    break;
×
1402
                }
×
1403
                case col_type_Binary: {
×
1404
                    ArrayBinary arr(m_alloc);
×
1405
                    ref_type ref = Array::get_as_ref(j);
×
1406
                    arr.init_from_ref(ref);
×
1407
                    std::cout << ", " << arr.get(i);
×
1408
                    break;
×
1409
                }
×
1410
                case col_type_Mixed: {
×
1411
                    ArrayMixed arr(m_alloc);
×
NEW
1412
                    set_string_interner(arr, col);
×
1413
                    ref_type ref = Array::get_as_ref(j);
×
1414
                    arr.init_from_ref(ref);
×
1415
                    std::cout << ", " << arr.get(i);
×
1416
                    break;
×
1417
                }
×
1418
                case col_type_Timestamp: {
×
1419
                    ArrayTimestamp arr(m_alloc);
×
1420
                    ref_type ref = Array::get_as_ref(j);
×
1421
                    arr.init_from_ref(ref);
×
1422
                    if (arr.is_null(i)) {
×
1423
                        std::cout << ", null";
×
1424
                    }
×
1425
                    else {
×
1426
                        std::cout << ", " << arr.get(i);
×
1427
                    }
×
1428
                    break;
×
1429
                }
×
1430
                case col_type_Decimal: {
×
1431
                    ArrayDecimal128 arr(m_alloc);
×
1432
                    ref_type ref = Array::get_as_ref(j);
×
1433
                    arr.init_from_ref(ref);
×
1434
                    if (arr.is_null(i)) {
×
1435
                        std::cout << ", null";
×
1436
                    }
×
1437
                    else {
×
1438
                        std::cout << ", " << arr.get(i);
×
1439
                    }
×
1440
                    break;
×
1441
                }
×
1442
                case col_type_ObjectId: {
×
1443
                    ArrayObjectIdNull arr(m_alloc);
×
1444
                    ref_type ref = Array::get_as_ref(j);
×
1445
                    arr.init_from_ref(ref);
×
1446
                    if (arr.is_null(i)) {
×
1447
                        std::cout << ", null";
×
1448
                    }
×
1449
                    else {
×
1450
                        std::cout << ", " << *arr.get(i);
×
1451
                    }
×
1452
                    break;
×
1453
                }
×
1454
                case col_type_UUID: {
×
1455
                    ArrayUUIDNull arr(m_alloc);
×
1456
                    ref_type ref = Array::get_as_ref(j);
×
1457
                    arr.init_from_ref(ref);
×
1458
                    if (arr.is_null(i)) {
×
1459
                        std::cout << ", null";
×
1460
                    }
×
1461
                    else {
×
1462
                        std::cout << ", " << arr.get(i);
×
1463
                    }
×
1464
                    break;
×
1465
                }
×
1466
                case col_type_Link: {
×
1467
                    ArrayKey arr(m_alloc);
×
1468
                    ref_type ref = Array::get_as_ref(j);
×
1469
                    arr.init_from_ref(ref);
×
1470
                    std::cout << ", " << arr.get(i);
×
1471
                    break;
×
1472
                }
×
1473
                case col_type_BackLink: {
×
1474
                    break;
×
1475
                }
×
1476
                default:
×
1477
                    std::cout << ", Error";
×
1478
                    break;
×
1479
            }
×
1480
            return IteratorControl::AdvanceToNext;
×
1481
        });
×
1482
        std::cout << std::endl;
×
1483
    }
×
1484
}
×
1485
// LCOV_EXCL_STOP
1486

1487
void Cluster::remove_backlinks(const Table* origin_table, ObjKey origin_key, ColKey origin_col_key,
1488
                               const std::vector<ObjKey>& keys, CascadeState& state)
1489
{
91,887✔
1490
    TableRef target_table = origin_table->get_opposite_table(origin_col_key);
91,887✔
1491
    ColKey backlink_col_key = origin_table->get_opposite_column(origin_col_key);
91,887✔
1492
    bool strong_links = target_table->is_embedded();
91,887✔
1493

1494
    for (auto key : keys) {
93,591✔
1495
        if (key != null_key) {
93,591✔
1496
            bool is_unres = key.is_unresolved();
93,591✔
1497
            Obj target_obj = is_unres ? target_table->m_tombstones->get(key) : target_table->m_clusters.get(key);
93,591✔
1498
            bool last_removed = target_obj.remove_one_backlink(backlink_col_key, origin_key); // Throws
93,591✔
1499
            if (is_unres) {
93,591✔
1500
                if (last_removed) {
30✔
1501
                    // Check is there are more backlinks
1502
                    if (!target_obj.has_backlinks(false)) {
24✔
1503
                        // Tombstones can be erased right away - there is no cascading effect
1504
                        target_table->m_tombstones->erase(key, state);
18✔
1505
                    }
18✔
1506
                }
24✔
1507
            }
30✔
1508
            else {
93,561✔
1509
                state.enqueue_for_cascade(target_obj, strong_links, last_removed);
93,561✔
1510
            }
93,561✔
1511
        }
93,591✔
1512
    }
93,591✔
1513
}
91,887✔
1514

1515
void Cluster::remove_backlinks(const Table* origin_table, ObjKey origin_key, ColKey origin_col_key,
1516
                               const std::vector<ObjLink>& links, CascadeState& state)
1517
{
144✔
1518
    Group* group = origin_table->get_parent_group();
144✔
1519
    TableKey origin_table_key = origin_table->get_key();
144✔
1520

1521
    for (auto link : links) {
240✔
1522
        if (link) {
240✔
1523
            bool is_unres = link.get_obj_key().is_unresolved();
240✔
1524
            Obj target_obj = group->get_object(link);
240✔
1525
            TableRef target_table = target_obj.get_table();
240✔
1526
            ColKey backlink_col_key = target_table->find_or_add_backlink_column(origin_col_key, origin_table_key);
240✔
1527

1528
            bool last_removed = target_obj.remove_one_backlink(backlink_col_key, origin_key); // Throws
240✔
1529
            if (is_unres) {
240✔
1530
                if (last_removed) {
6✔
1531
                    // Check if there are more backlinks
1532
                    if (!target_obj.has_backlinks(false)) {
6✔
1533
                        // Tombstones can be erased right away - there is no cascading effect
1534
                        target_table->m_tombstones->erase(link.get_obj_key(), state);
6✔
1535
                    }
6✔
1536
                }
6✔
1537
            }
6✔
1538
            else {
234✔
1539
                state.enqueue_for_cascade(target_obj, false, last_removed);
234✔
1540
            }
234✔
1541
        }
240✔
1542
    }
240✔
1543
}
144✔
1544

1545
namespace {
1546

1547
template <typename Fn>
1548
static void switch_on_type(ColKey ck, Fn&& fn)
1549
{
1,640,910✔
1550
    bool is_optional = ck.is_nullable();
1,640,910✔
1551
    auto type = ck.get_type();
1,640,910✔
1552
    switch (type) {
1,640,910✔
1553
        case col_type_Int:
855,921✔
1554
            return is_optional ? fn((util::Optional<int64_t>*)0) : fn((int64_t*)0);
855,921✔
1555
        case col_type_Bool:
9,315✔
1556
            return is_optional ? fn((util::Optional<bool>*)0) : fn((bool*)0);
9,315✔
1557
        case col_type_Float:
9,522✔
1558
            return is_optional ? fn((util::Optional<float>*)0) : fn((float*)0);
9,522✔
1559
        case col_type_Double:
13,527✔
1560
            return is_optional ? fn((util::Optional<double>*)0) : fn((double*)0);
13,527✔
1561
        case col_type_String:
275,343✔
1562
            return fn((StringData*)0);
275,343✔
1563
        case col_type_Binary:
252,390✔
1564
            return fn((BinaryData*)0);
252,390✔
1565
        case col_type_Timestamp:
41,163✔
1566
            return fn((Timestamp*)0);
41,163✔
1567
        case col_type_Link:
58,305✔
1568
            return fn((ObjKey*)0);
58,305✔
1569
        case col_type_ObjectId:
88,860✔
1570
            return is_optional ? fn((util::Optional<ObjectId>*)0) : fn((ObjectId*)0);
88,860✔
1571
        case col_type_Decimal:
8,559✔
1572
            return fn((Decimal128*)0);
8,559✔
1573
        case col_type_UUID:
9,339✔
1574
            return is_optional ? fn((util::Optional<UUID>*)0) : fn((UUID*)0);
9,339✔
1575
        case col_type_Mixed:
18,687✔
1576
            return fn((Mixed*)0);
18,687✔
1577
        default:
✔
1578
            REALM_COMPILER_HINT_UNREACHABLE();
×
1579
    }
1,640,910✔
1580
}
1,640,910✔
1581

1582
} // namespace
1583

1584
ref_type Cluster::typed_write(ref_type ref, _impl::ArrayWriterBase& out) const
1585
{
1,090,830✔
1586
    REALM_ASSERT_DEBUG(ref == get_mem().get_ref());
1,090,830✔
1587
    bool only_modified = out.only_modified;
1,090,830✔
1588
    if (only_modified && m_alloc.is_read_only(ref))
1,090,830✔
1589
        return ref;
1,062✔
1590
    REALM_ASSERT_DEBUG(!get_is_inner_bptree_node_from_header(get_header()));
1,089,768✔
1591
    REALM_ASSERT_DEBUG(!get_context_flag_from_header(get_header()));
1,089,768✔
1592
    TempArray written_cluster(size());
1,089,768✔
1593
    for (size_t j = 0; j < size(); ++j) {
4,816,515✔
1594
        RefOrTagged leaf_rot = get_as_ref_or_tagged(j);
3,726,753✔
1595
        // Handle nulls
1596
        if (!leaf_rot.is_ref() || !leaf_rot.get_as_ref()) {
3,726,753✔
1597
            written_cluster.set(j, leaf_rot);
1,020,243✔
1598
            continue;
1,020,243✔
1599
        }
1,020,243✔
1600
        // prune subtrees which should not be written:
1601
        if (only_modified && m_alloc.is_read_only(leaf_rot.get_as_ref())) {
2,706,510✔
1602
            written_cluster.set(j, leaf_rot);
374,478✔
1603
            continue;
374,478✔
1604
        }
374,478✔
1605
        // from here: this leaf exists and needs to be written.
1606
        ref = leaf_rot.get_as_ref();
2,332,032✔
1607
        ref_type new_ref = ref;
2,332,032✔
1608
        if (j == 0) {
2,332,032✔
1609
            // Keys  (ArrayUnsigned me thinks, so don't compress)
1610
            Array leaf(m_alloc);
53,739✔
1611
            leaf.init_from_ref(ref);
53,739✔
1612
            new_ref = leaf.write(out, false, only_modified, false);
53,739✔
1613
        }
53,739✔
1614
        else {
2,278,293✔
1615
            // Columns
1616
            auto col_key = out.table->m_leaf_ndx2colkey[j - 1];
2,278,293✔
1617
            out.col_key = col_key;
2,278,293✔
1618
            auto col_type = col_key.get_type();
2,278,293✔
1619
            if (col_key.is_collection()) {
2,278,293✔
1620
                ArrayRef arr_ref(m_alloc);
457,896✔
1621
                arr_ref.init_from_ref(ref);
457,896✔
1622
                auto sz = arr_ref.size();
457,896✔
1623
                TempArray written_ref_leaf(sz);
457,896✔
1624

1625
                for (size_t k = 0; k < sz; k++) {
4,839,678✔
1626
                    ref_type new_sub_ref = 0;
4,381,788✔
1627
                    // Now we have to find out if the nested collection is a
1628
                    // dictionary or a list. If the top array has a size of 2
1629
                    // and it is not a BplusTree inner node, then it is a dictionary
1630
                    if (auto sub_ref = arr_ref.get(k)) {
4,381,788✔
1631
                        if (col_key.is_dictionary()) {
2,655,858✔
1632
                            new_sub_ref = Dictionary::typed_write(sub_ref, out, m_alloc);
44,097✔
1633
                        }
44,097✔
1634
                        else {
2,611,761✔
1635
                            // List or set - Can be handled the same way
1636
                            // For some reason, switch_on_type() would not compile on Windows
1637
                            // switch_on_type(col_key, [&](auto t) {
1638
                            //     using U = std::decay_t<decltype(*t)>;
1639
                            //     new_sub_ref = BPlusTree<U>::typed_write(sub_ref, out, m_alloc);
1640
                            // });
1641
                            switch (col_type) {
2,611,761✔
1642
                                case col_type_Int:
27,165✔
1643
                                    new_sub_ref = BPlusTree<int64_t>::typed_write(sub_ref, out, m_alloc);
27,165✔
1644
                                    break;
27,165✔
1645
                                case col_type_Bool:
2,124✔
1646
                                    new_sub_ref = BPlusTree<bool>::typed_write(sub_ref, out, m_alloc);
2,124✔
1647
                                    break;
2,124✔
1648
                                case col_type_Float:
2,208✔
1649
                                    new_sub_ref = BPlusTree<float>::typed_write(sub_ref, out, m_alloc);
2,208✔
1650
                                    break;
2,208✔
1651
                                case col_type_Double:
2,406✔
1652
                                    new_sub_ref = BPlusTree<double>::typed_write(sub_ref, out, m_alloc);
2,406✔
1653
                                    break;
2,406✔
1654
                                case col_type_String:
6,504✔
1655
                                    new_sub_ref = BPlusTree<StringData>::typed_write(sub_ref, out, m_alloc);
6,504✔
1656
                                    break;
6,504✔
1657
                                case col_type_Binary:
19,770✔
1658
                                    new_sub_ref = BPlusTree<BinaryData>::typed_write(sub_ref, out, m_alloc);
19,770✔
1659
                                    break;
19,770✔
1660
                                case col_type_Timestamp:
2,154✔
1661
                                    new_sub_ref = BPlusTree<Timestamp>::typed_write(sub_ref, out, m_alloc);
2,154✔
1662
                                    break;
2,154✔
1663
                                case col_type_Link:
2,524,563✔
1664
                                    new_sub_ref = BPlusTree<ObjKey>::typed_write(sub_ref, out, m_alloc);
2,524,563✔
1665
                                    break;
2,524,563✔
1666
                                case col_type_ObjectId:
13,512✔
1667
                                    new_sub_ref = BPlusTree<ObjectId>::typed_write(sub_ref, out, m_alloc);
13,512✔
1668
                                    break;
13,512✔
1669
                                case col_type_Decimal:
2,328✔
1670
                                    new_sub_ref = BPlusTree<Decimal128>::typed_write(sub_ref, out, m_alloc);
2,328✔
1671
                                    break;
2,328✔
1672
                                case col_type_UUID:
2,148✔
1673
                                    new_sub_ref = BPlusTree<UUID>::typed_write(sub_ref, out, m_alloc);
2,148✔
1674
                                    break;
2,148✔
1675
                                case col_type_Mixed:
6,912✔
1676
                                    new_sub_ref = BPlusTree<Mixed>::typed_write(sub_ref, out, m_alloc);
6,912✔
1677
                                    break;
6,912✔
1678
                                default:
✔
1679
                                    REALM_COMPILER_HINT_UNREACHABLE();
×
1680
                            }
2,611,761✔
1681
                        }
2,611,761✔
1682
                    }
2,655,858✔
1683
                    written_ref_leaf.set_as_ref(k, new_sub_ref);
4,381,782✔
1684
                }
4,381,782✔
1685
                new_ref = written_ref_leaf.write(out);
457,890✔
1686
            }
457,890✔
1687
            else if (col_type == col_type_BackLink) {
1,820,397✔
1688
                Array leaf(m_alloc);
179,523✔
1689
                leaf.init_from_ref(ref);
179,523✔
1690
                new_ref = leaf.write(out, true, only_modified, false);
179,523✔
1691
            }
179,523✔
1692
            else {
1,640,874✔
1693
                switch_on_type(col_key, [&](auto t) {
1,640,928✔
1694
                    using U = std::decay_t<decltype(*t)>;
1,640,928✔
1695
                    new_ref = ColumnTypeTraits<U>::cluster_leaf_type::typed_write(ref, out, m_alloc);
1,640,928✔
1696
                });
1,640,928✔
1697
            }
1,640,874✔
1698
        }
2,278,293✔
1699
        written_cluster.set_as_ref(j, new_ref);
2,332,026✔
1700
    }
2,332,026✔
1701
    return written_cluster.write(out);
1,089,762✔
1702
}
1,089,768✔
1703
} // 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