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

realm / realm-core / jorgen.edelbo_338

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

Pull #7803

Evergreen

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

103028 of 180606 branches covered (57.05%)

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

155 existing lines in 24 files now uncovered.

218583 of 240583 relevant lines covered (90.86%)

7959624.7 hits per line

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

91.38
/src/realm/array_backlink.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/array_backlink.hpp>
20
#include <realm/util/assert.hpp>
21
#include <realm/table.hpp>
22
#include <realm/group.hpp>
23
#include <realm/list.hpp>
24
#include <realm/set.hpp>
25
#include <realm/dictionary.hpp>
26

27
using namespace realm;
28

29
// nullify forward links corresponding to any backward links at index 'ndx'.
30
void ArrayBacklink::nullify_fwd_links(size_t ndx, CascadeState& state)
31
{
235,722✔
32
    uint64_t value = Array::get(ndx);
235,722✔
33
    if (value == 0) {
235,722✔
34
        return;
229,323✔
35
    }
229,323✔
36

37
    // Naming: Links go from source to target.
38
    // Backlinks go from target to source.
39
    // This array holds backlinks, hence it is the target.
40
    // The table which holds the corresponding fwd links is the source.
41

42
    // determine target table, column and key.
43
    REALM_ASSERT_DEBUG(dynamic_cast<Cluster*>(get_parent()));
6,399✔
44
    auto cluster = static_cast<Cluster*>(get_parent());
6,399✔
45
    const Table* target_table = cluster->get_owning_table();
6,399✔
46
    ColKey target_col_key = cluster->get_col_key(get_ndx_in_parent());
6,399✔
47
    ObjKey target_key = cluster->get_real_key(ndx);
6,399✔
48

49
    // determine the source table/col - which is the one holding the forward links
50
    TableRef source_table = target_table->get_opposite_table(target_col_key);
6,399✔
51
    ColKey src_col_key = target_table->get_opposite_column(target_col_key);
6,399✔
52

53
    // Now follow all backlinks to their origin and clear forward links.
54
    if ((value & 1) != 0) {
6,399✔
55
        // just a single one
56
        state.enqueue_for_nullification(*source_table, src_col_key, ObjKey(value >> 1),
5,799✔
57
                                        {target_table->get_key(), target_key});
5,799✔
58
    }
5,799✔
59
    else {
600✔
60
        // There is more than one backlink - Iterate through them all
61
        ref_type ref = to_ref(value);
600✔
62
        BPlusTree<int64_t> backlink_list(m_alloc);
600✔
63
        backlink_list.init_from_ref(ref);
600✔
64

65
        size_t sz = backlink_list.size();
600✔
66
        for (size_t i = 0; i < sz; i++) {
1,800✔
67
            state.enqueue_for_nullification(*source_table, src_col_key, ObjKey(backlink_list.get(i)),
1,200✔
68
                                            {target_table->get_key(), target_key});
1,200✔
69
        }
1,200✔
70
    }
600✔
71
}
6,399✔
72

73
void ArrayBacklink::add(size_t ndx, ObjKey key)
74
{
7,057,461✔
75
    uint64_t value = Array::get(ndx);
7,057,461✔
76

77
    // A backlink list of size 1 is stored as a single non-ref column value.
78
    if (value == 0) {
7,057,461✔
79
        set(ndx, key.value << 1 | 1); // Throws
802,734✔
80
        return;
802,734✔
81
    }
802,734✔
82

83
    // When increasing the size of the backlink list from 1 to 2, we need to
84
    // convert from the single non-ref column value representation, to a B+-tree
85
    // representation.
86
    BPlusTree<int64_t> backlink_list(m_alloc);
6,254,727✔
87
    if ((value & 1) != 0) {
6,254,727✔
88
        // Create new column to hold backlinks
89
        backlink_list.create();
61,998✔
90
        set_as_ref(ndx, backlink_list.get_ref());
61,998✔
91
        backlink_list.add(value >> 1);
61,998✔
92
    }
61,998✔
93
    else {
6,192,729✔
94
        backlink_list.init_from_ref(to_ref(value));
6,192,729✔
95
        backlink_list.set_parent(this, ndx);
6,192,729✔
96
        backlink_list.split_if_needed();
6,192,729✔
97
    }
6,192,729✔
98
    backlink_list.add(key.value); // Throws
6,254,727✔
99
}
6,254,727✔
100

101
// Return true if the last link was removed
102
bool ArrayBacklink::remove(size_t ndx, ObjKey key)
103
{
509,451✔
104
    uint64_t value = Array::get(ndx);
509,451✔
105
    REALM_ASSERT_DEBUG(value != 0);
509,451✔
106
    if (value == 0)
509,451✔
107
        return true;
×
108

109
    // If there is only a single backlink, it can be stored as
110
    // a tagged value
111
    if ((value & 1) != 0) {
509,451✔
112
        REALM_ASSERT_DEBUG(int64_t(value >> 1) == key.value);
254,448✔
113
        if (int64_t(value >> 1) == key.value) {
254,448✔
114
            set(ndx, 0);
254,448✔
115
            return true;
254,448✔
116
        }
254,448✔
117
        return false;
×
118
    }
254,448✔
119

120
    // if there is a list of backlinks we have to find
121
    // the right one and remove it.
122
    BPlusTree<int64_t> backlink_list(m_alloc);
255,003✔
123
    backlink_list.init_from_ref(ref_type(value));
255,003✔
124
    backlink_list.set_parent(this, ndx);
255,003✔
125
    backlink_list.split_if_needed();
255,003✔
126

127
    size_t last_ndx = backlink_list.size() - 1;
255,003✔
128
    size_t backlink_ndx = backlink_list.find_first(key.value);
255,003✔
129
    REALM_ASSERT_DEBUG(backlink_ndx != not_found);
255,003✔
130
    if (backlink_ndx != not_found) {
255,210✔
131
        if (backlink_ndx != last_ndx)
255,210✔
132
            backlink_list.set(backlink_ndx, backlink_list.get(last_ndx));
148,980✔
133
        backlink_list.erase(last_ndx); // Throws
255,210✔
134
    }
255,210✔
135

136
    // If there is only one backlink left we can inline it as tagged value
137
    if (last_ndx == 1) {
255,003✔
138
        uint64_t key_value = backlink_list.get(0);
44,340✔
139
        backlink_list.destroy();
44,340✔
140

141
        set(ndx, key_value << 1 | 1);
44,340✔
142
    }
44,340✔
143

144
    return false;
255,003✔
145
}
509,451✔
146

147
void ArrayBacklink::erase(size_t ndx)
148
{
249,804✔
149
    uint64_t value = Array::get(ndx);
249,804✔
150
    if (value && (value & 1) == 0) {
249,804✔
151
        Array::destroy_deep(ref_type(value), m_alloc);
438✔
152
    }
438✔
153
    Array::erase(ndx);
249,804✔
154
}
249,804✔
155

156
size_t ArrayBacklink::get_backlink_count(size_t ndx) const
157
{
1,501,389✔
158
    uint64_t value = Array::get(ndx);
1,501,389✔
159
    if (value == 0) {
1,501,389✔
160
        return 0;
647,025✔
161
    }
647,025✔
162

163
    // If there is only a single backlink, it can be stored as
164
    // a tagged value
165
    if ((value & 1) != 0) {
854,364✔
166
        return 1;
622,335✔
167
    }
622,335✔
168

169
    // return size of list
170
    MemRef mem(ref_type(value), m_alloc);
232,029✔
171
    return BPlusTree<int64_t>::size_from_header(mem.get_addr());
232,029✔
172
}
854,364✔
173

174
ObjKey ArrayBacklink::get_backlink(size_t ndx, size_t index) const
175
{
9,404,925✔
176
    uint64_t value = Array::get(ndx);
9,404,925✔
177
    REALM_ASSERT(value != 0);
9,404,925✔
178

179
    // If there is only a single backlink, it can be stored as
180
    // a tagged value
181
    if ((value & 1) != 0) {
9,404,925✔
182
        REALM_ASSERT(index == 0);
212,904✔
183
        return ObjKey(int64_t(value >> 1));
212,904✔
184
    }
212,904✔
185

186
    BPlusTree<int64_t> backlink_list(m_alloc);
9,192,021✔
187
    backlink_list.init_from_ref(ref_type(value));
9,192,021✔
188

189
    REALM_ASSERT(index < backlink_list.size());
9,192,021✔
190
    return ObjKey(backlink_list.get(index));
9,192,021✔
191
}
9,404,925✔
192

193
void ArrayBacklink::verify() const
194
{
2,598✔
195
#ifdef REALM_DEBUG
2,598✔
196
    Array::verify();
2,598✔
197

198
    REALM_ASSERT(dynamic_cast<Cluster*>(get_parent()));
2,598✔
199
    auto cluster = static_cast<Cluster*>(get_parent());
2,598✔
200
    const Table* target_table = cluster->get_owning_table();
2,598✔
201
    ColKey backlink_col_key = cluster->get_col_key(get_ndx_in_parent());
2,598✔
202

203
    TableRef src_table = target_table->get_opposite_table(backlink_col_key);
2,598✔
204
    ColKey src_col_key = target_table->get_opposite_column(backlink_col_key);
2,598✔
205

206
    // Verify that each backlink has a corresponding forward link
207
    for (size_t i = 0; i < size(); ++i) {
38,337✔
208
        ObjKey target_key = cluster->get_real_key(i);
35,739✔
209
        ObjLink target_link(target_table->get_key(), target_key);
35,739✔
210
        auto cnt = get_backlink_count(i);
35,739✔
211
        for (size_t j = 0; j < cnt; ++j) {
124,218✔
212
            Obj src_obj = src_table->get_object(get_backlink(i, j));
88,479✔
213
            if (src_col_key.is_dictionary()) {
88,479✔
214
                REALM_ASSERT(src_obj.get_dictionary_ptr(src_col_key)->find_any(target_link) != npos);
318✔
215
                continue;
318✔
216
            }
318✔
217

218
            if (src_col_key.get_type() == col_type_Mixed) {
88,161✔
219
                if (src_col_key.is_collection()) {
24✔
220
                    REALM_ASSERT(src_obj.get_collection_ptr(src_col_key)->find_any(target_link) != npos);
18✔
221
                }
18✔
222
                else {
6✔
223
                    auto val = src_obj.get<Mixed>(src_col_key);
6✔
224
                    if (val.is_type(type_TypedLink)) {
6✔
225
                        REALM_ASSERT(src_obj.get<Mixed>(src_col_key).get_link() == target_link);
6✔
226
                    }
6✔
227
                    else if (val.is_type(type_List)) {
×
NEW
228
                        DummyParent parent(src_table, val.get_ref(), src_col_key);
×
229
                        Lst<Mixed> list(parent, 0);
×
230
                        REALM_ASSERT(list.find_any(target_link) != npos);
×
231
                    }
×
232
                    else if (val.is_type(type_Dictionary)) {
×
NEW
233
                        DummyParent parent(src_table, val.get_ref(), src_col_key);
×
234
                        Dictionary dict(parent, 0);
×
235
                        REALM_ASSERT(dict.find_any(target_link) != npos);
×
236
                    }
×
237
                }
6✔
238
                continue;
24✔
239
            }
24✔
240

241
            if (src_col_key.is_list()) {
88,137✔
242
                REALM_ASSERT(src_obj.get_list<ObjKey>(src_col_key).find_first(target_key) != npos);
27,438✔
243
            }
27,438✔
244
            else if (src_col_key.is_set()) {
60,699✔
245
                REALM_ASSERT(src_obj.get_set<ObjKey>(src_col_key).find(target_key) != npos);
×
246
            }
×
247
            else {
60,699✔
248
                REALM_ASSERT(src_obj.get_unfiltered_link(src_col_key) == target_key);
60,699✔
249
            }
60,699✔
250
        }
88,137✔
251
    }
35,739✔
252
#endif
2,598✔
253
}
2,598✔
254

255
bool ArrayBacklink::verify_backlink(size_t ndx, int64_t link)
256
{
120,714✔
257
#ifdef REALM_DEBUG
120,714✔
258
    uint64_t value = Array::get(ndx);
120,714✔
259
    if (value == 0)
120,714✔
260
        return false;
×
261

262
    // If there is only a single backlink, it can be stored as
263
    // a tagged value
264
    if ((value & 1) != 0) {
120,714✔
265
        return int64_t(value >> 1) == link;
564✔
266
    }
564✔
267

268
    BPlusTree<int64_t> backlink_list(m_alloc);
120,150✔
269
    backlink_list.init_from_ref(ref_type(value));
120,150✔
270

271
    return backlink_list.find_first(link) != realm::not_found;
120,150✔
272
#else
273
    static_cast<void>(ndx);
274
    static_cast<void>(link);
275
    return true;
276
#endif
277
}
120,714✔
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