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

realm / realm-core / 1660

11 Sep 2023 02:28PM UTC coverage: 91.236% (+0.02%) from 91.215%
1660

push

Evergreen

GitHub
make DB::get_number_of_versions() reflect the number of live versions (#6960)

95844 of 175764 branches covered (0.0%)

21 of 21 new or added lines in 2 files covered. (100.0%)

83 existing lines in 11 files now uncovered.

233544 of 255978 relevant lines covered (91.24%)

8147599.88 hits per line

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

95.9
/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
{
188,661✔
32
    uint64_t value = Array::get(ndx);
188,661✔
33
    if (value == 0) {
188,661✔
34
        return;
183,966✔
35
    }
183,966✔
36

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

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

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

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

276✔
65
        size_t sz = backlink_list.size();
552✔
66
        for (size_t i = 0; i < sz; i++) {
1,662✔
67
            state.enqueue_for_nullification(*source_table, src_col_key, ObjKey(backlink_list.get(i)),
1,110✔
68
                                            {target_table->get_key(), target_key});
1,110✔
69
        }
1,110✔
70
    }
552✔
71
}
4,695✔
72

73
void ArrayBacklink::add(size_t ndx, ObjKey key)
74
{
6,729,030✔
75
    uint64_t value = Array::get(ndx);
6,729,030✔
76

3,364,785✔
77
    // A backlink list of size 1 is stored as a single non-ref column value.
3,364,785✔
78
    if (value == 0) {
6,729,030✔
79
        set(ndx, key.value << 1 | 1); // Throws
614,445✔
80
        return;
614,445✔
81
    }
614,445✔
82

3,057,984✔
83
    // When increasing the size of the backlink list from 1 to 2, we need to
3,057,984✔
84
    // convert from the single non-ref column value representation, to a B+-tree
3,057,984✔
85
    // representation.
3,057,984✔
86
    Array backlink_list(m_alloc);
6,114,585✔
87
    if ((value & 1) != 0) {
6,114,585✔
88
        // Create new column to hold backlinks
10,038✔
89
        backlink_list.create(Array::type_Normal);
20,055✔
90
        set_as_ref(ndx, backlink_list.get_ref());
20,055✔
91
        backlink_list.add(value >> 1);
20,055✔
92
    }
20,055✔
93
    else {
6,094,530✔
94
        backlink_list.init_from_ref(to_ref(value));
6,094,530✔
95
        backlink_list.set_parent(this, ndx);
6,094,530✔
96
    }
6,094,530✔
97
    backlink_list.add(key.value); // Throws
6,114,585✔
98
}
6,114,585✔
99

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

71,784✔
108
    // If there is only a single backlink, it can be stored as
71,784✔
109
    // a tagged value
71,784✔
110
    if ((value & 1) != 0) {
143,577✔
111
        REALM_ASSERT_DEBUG(int64_t(value >> 1) == key.value);
74,619✔
112
        if (int64_t(value >> 1) == key.value) {
74,619✔
113
            set(ndx, 0);
74,619✔
114
            return true;
74,619✔
115
        }
74,619✔
UNCOV
116
        return false;
×
UNCOV
117
    }
×
118

34,500✔
119
    // if there is a list of backlinks we have to find
34,500✔
120
    // the right one and remove it.
34,500✔
121
    Array backlink_list(m_alloc);
68,958✔
122
    backlink_list.init_from_ref(ref_type(value));
68,958✔
123
    backlink_list.set_parent(this, ndx);
68,958✔
124

34,500✔
125
    size_t last_ndx = backlink_list.size() - 1;
68,958✔
126
    size_t backlink_ndx = backlink_list.find_first(key.value);
68,958✔
127
    REALM_ASSERT_DEBUG(backlink_ndx != not_found);
68,958✔
128
    if (backlink_ndx != not_found) {
68,961✔
129
        if (backlink_ndx != last_ndx)
68,961✔
130
            backlink_list.set(backlink_ndx, backlink_list.get(last_ndx));
38,133✔
131
        backlink_list.truncate(last_ndx); // Throws
68,961✔
132
    }
68,961✔
133

34,500✔
134
    // If there is only one backlink left we can inline it as tagged value
34,500✔
135
    if (last_ndx == 1) {
68,958✔
136
        uint64_t key_value = backlink_list.get(0);
2,184✔
137
        backlink_list.destroy();
2,184✔
138

1,122✔
139
        set(ndx, key_value << 1 | 1);
2,184✔
140
    }
2,184✔
141

34,500✔
142
    return false;
68,958✔
143
}
68,958✔
144

145
void ArrayBacklink::erase(size_t ndx)
146
{
202,698✔
147
    uint64_t value = Array::get(ndx);
202,698✔
148
    if (value && (value & 1) == 0) {
202,698✔
149
        Array::destroy(ref_type(value), m_alloc);
396✔
150
    }
396✔
151
    Array::erase(ndx);
202,698✔
152
}
202,698✔
153

154
size_t ArrayBacklink::get_backlink_count(size_t ndx) const
155
{
677,067✔
156
    uint64_t value = Array::get(ndx);
677,067✔
157
    if (value == 0) {
677,067✔
158
        return 0;
358,230✔
159
    }
358,230✔
160

159,330✔
161
    // If there is only a single backlink, it can be stored as
159,330✔
162
    // a tagged value
159,330✔
163
    if ((value & 1) != 0) {
318,837✔
164
        return 1;
313,785✔
165
    }
313,785✔
166

2,526✔
167
    // return size of list
2,526✔
168
    MemRef mem(ref_type(value), m_alloc);
5,052✔
169
    return Array::get_size_from_header(mem.get_addr());
5,052✔
170
}
5,052✔
171

172
ObjKey ArrayBacklink::get_backlink(size_t ndx, size_t index) const
173
{
264,582✔
174
    uint64_t value = Array::get(ndx);
264,582✔
175
    REALM_ASSERT(value != 0);
264,582✔
176

132,264✔
177
    // If there is only a single backlink, it can be stored as
132,264✔
178
    // a tagged value
132,264✔
179
    if ((value & 1) != 0) {
264,582✔
180
        REALM_ASSERT(index == 0);
173,298✔
181
        return ObjKey(int64_t(value >> 1));
173,298✔
182
    }
173,298✔
183

45,642✔
184
    Array backlink_list(m_alloc);
91,284✔
185
    backlink_list.init_from_ref(ref_type(value));
91,284✔
186

45,642✔
187
    REALM_ASSERT(index < backlink_list.size());
91,284✔
188
    return ObjKey(backlink_list.get(index));
91,284✔
189
}
91,284✔
190

191
void ArrayBacklink::verify() const
192
{
2,667✔
193
#ifdef REALM_DEBUG
2,667✔
194
    Array::verify();
2,667✔
195

1,308✔
196
    REALM_ASSERT(dynamic_cast<Cluster*>(get_parent()));
2,667✔
197
    auto cluster = static_cast<Cluster*>(get_parent());
2,667✔
198
    const Table* target_table = cluster->get_owning_table();
2,667✔
199
    ColKey backlink_col_key = cluster->get_col_key(get_ndx_in_parent());
2,667✔
200

1,308✔
201
    TableRef src_table = target_table->get_opposite_table(backlink_col_key);
2,667✔
202
    ColKey src_col_key = target_table->get_opposite_column(backlink_col_key);
2,667✔
203

1,308✔
204
    // Verify that each backlink has a corresponding forward link
1,308✔
205
    for (size_t i = 0; i < size(); ++i) {
27,879✔
206
        ObjKey target_key = cluster->get_real_key(i);
25,212✔
207
        ObjLink target_link(target_table->get_key(), target_key);
25,212✔
208
        auto cnt = get_backlink_count(i);
25,212✔
209
        for (size_t j = 0; j < cnt; ++j) {
36,486✔
210
            Obj src_obj = src_table->get_object(get_backlink(i, j));
11,274✔
211
            if (src_col_key.is_dictionary()) {
11,274✔
212
                REALM_ASSERT(src_obj.get_dictionary_ptr(src_col_key)->find_any(target_link) != npos);
288✔
213
                continue;
288✔
214
            }
288✔
215

5,493✔
216
            if (src_col_key.get_type() == col_type_Mixed) {
10,986✔
217
                if (src_col_key.is_collection()) {
6✔
218
                    REALM_ASSERT(src_obj.get_collection_ptr(src_col_key)->find_any(target_link) != npos);
6✔
219
                }
6✔
220
                else {
×
221
                    REALM_ASSERT(src_obj.get<Mixed>(src_col_key).get_link() == target_link);
×
222
                }
×
223
                continue;
6✔
224
            }
6✔
225

5,490✔
226
            if (src_col_key.is_list()) {
10,980✔
227
                REALM_ASSERT(src_obj.get_list<ObjKey>(src_col_key).find_first(target_key) != npos);
10,092✔
228
            }
10,092✔
229
            else if (src_col_key.is_set()) {
888✔
230
                REALM_ASSERT(src_obj.get_set<ObjKey>(src_col_key).find(target_key) != npos);
×
231
            }
×
232
            else {
888✔
233
                REALM_ASSERT(src_obj.get_unfiltered_link(src_col_key) == target_key);
888✔
234
            }
888✔
235
        }
10,980✔
236
    }
25,212✔
237
#endif
2,667✔
238
}
2,667✔
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

© 2026 Coveralls, Inc