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

realm / realm-core / 1724

02 Oct 2023 06:18PM UTC coverage: 91.638% (+0.5%) from 91.132%
1724

push

Evergreen

web-flow
Merge pull request #7003 from realm/tg/asymmetric-object-list

Allow collections of non-embedded objects in asymmetric objects

94276 of 173418 branches covered (0.0%)

281 of 281 new or added lines in 7 files covered. (100.0%)

109 existing lines in 16 files now uncovered.

230320 of 251336 relevant lines covered (91.64%)

6686453.29 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
{
189,048✔
32
    uint64_t value = Array::get(ndx);
189,048✔
33
    if (value == 0) {
189,048✔
34
        return;
184,353✔
35
    }
184,353✔
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,730,077✔
75
    uint64_t value = Array::get(ndx);
6,730,077✔
76

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

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

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

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

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

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

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

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

34,533✔
142
    return false;
69,090✔
143
}
69,090✔
144

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

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

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

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

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

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

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

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

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

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

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

1,302✔
204
    // Verify that each backlink has a corresponding forward link
1,302✔
205
    for (size_t i = 0; i < size(); ++i) {
29,208✔
206
        ObjKey target_key = cluster->get_real_key(i);
26,622✔
207
        ObjLink target_link(target_table->get_key(), target_key);
26,622✔
208
        auto cnt = get_backlink_count(i);
26,622✔
209
        for (size_t j = 0; j < cnt; ++j) {
37,896✔
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
    }
26,622✔
237
#endif
2,586✔
238
}
2,586✔
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