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

realm / realm-core / github_pull_request_281750

30 Oct 2023 03:37PM UTC coverage: 90.528% (-1.0%) from 91.571%
github_pull_request_281750

Pull #6073

Evergreen

jedelbo
Log free space and history sizes when opening file
Pull Request #6073: Merge next-major

95488 of 175952 branches covered (0.0%)

8973 of 12277 new or added lines in 149 files covered. (73.09%)

622 existing lines in 51 files now uncovered.

233503 of 257934 relevant lines covered (90.53%)

6533720.56 hits per line

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

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

19

20
#include "realm/set.hpp"
21
#include "realm/array_basic.hpp"
22
#include "realm/array_integer.hpp"
23
#include "realm/array_bool.hpp"
24
#include "realm/array_string.hpp"
25
#include "realm/array_binary.hpp"
26
#include "realm/array_timestamp.hpp"
27
#include "realm/array_decimal128.hpp"
28
#include "realm/array_fixed_bytes.hpp"
29
#include "realm/array_typed_link.hpp"
30
#include "realm/array_mixed.hpp"
31
#include "realm/replication.hpp"
32

33
namespace realm {
34

35
/********************************** SetBase *********************************/
36

37
void SetBase::insert_repl(Replication* repl, size_t index, Mixed value) const
38
{
60,951✔
39
    repl->set_insert(*this, index, value);
60,951✔
40
}
60,951✔
41

42
void SetBase::erase_repl(Replication* repl, size_t index, Mixed value) const
43
{
9,456✔
44
    repl->set_erase(*this, index, value);
9,456✔
45
}
9,456✔
46

47
void SetBase::clear_repl(Replication* repl) const
48
{
2,412✔
49
    repl->set_clear(*this);
2,412✔
50
}
2,412✔
51

52
std::vector<Mixed> SetBase::convert_to_mixed_set(const CollectionBase& rhs)
53
{
522✔
54
    std::vector<Mixed> mixed;
522✔
55
    mixed.reserve(rhs.size());
522✔
56
    for (size_t i = 0; i < rhs.size(); i++) {
2,754✔
57
        mixed.push_back(rhs.get_any(i));
2,232✔
58
    }
2,232✔
59
    std::sort(mixed.begin(), mixed.end(), SetElementLessThan<Mixed>());
522✔
60
    mixed.erase(std::unique(mixed.begin(), mixed.end()), mixed.end());
522✔
61
    return mixed;
522✔
62
}
522✔
63

64
template <>
65
void CollectionBaseImpl<SetBase>::to_json(std::ostream& out, size_t, JSONOutputMode output_mode,
66
                                          util::FunctionRef<void(const Mixed&)> fn) const
67
{
300✔
68
    int closing = 0;
300✔
69
    if (output_mode == output_mode_xjson_plus) {
300✔
70
        out << "{ \"$set\": ";
90✔
71
        closing++;
90✔
72
    }
90✔
73

150✔
74
    out << "[";
300✔
75
    auto sz = size();
300✔
76
    for (size_t i = 0; i < sz; i++) {
606✔
77
        if (i > 0)
306✔
78
            out << ",";
6✔
79
        Mixed val = get_any(i);
306✔
80
        if (val.is_type(type_TypedLink)) {
306✔
81
            fn(val);
6✔
82
        }
6✔
83
        else {
300✔
84
            val.to_json(out, output_mode);
300✔
85
        }
300✔
86
    }
306✔
87
    out << "]";
300✔
88
    while (closing--)
390✔
89
        out << "}";
90✔
90
}
300✔
91

92
/********************************* Set<Key> *********************************/
93

94
template <>
95
void Set<ObjKey>::do_insert(size_t ndx, ObjKey target_key)
96
{
16,938✔
97
    auto origin_table = get_table_unchecked();
16,938✔
98
    auto target_table_key = origin_table->get_opposite_table_key(m_col_key);
16,938✔
99
    set_backlink(m_col_key, {target_table_key, target_key});
16,938✔
100
    m_tree->insert(ndx, target_key);
16,938✔
101
    if (target_key.is_unresolved()) {
16,938✔
102
        m_tree->set_context_flag(true);
54✔
103
    }
54✔
104
}
16,938✔
105

106
template <>
107
void Set<ObjKey>::do_erase(size_t ndx)
108
{
5,592✔
109
    auto origin_table = get_table_unchecked();
5,592✔
110
    auto target_table_key = origin_table->get_opposite_table_key(m_col_key);
5,592✔
111
    ObjKey old_key = get(ndx);
5,592✔
112
    CascadeState state(old_key.is_unresolved() ? CascadeState::Mode::All : CascadeState::Mode::Strong);
5,586✔
113

2,796✔
114
    bool recurse = remove_backlink(m_col_key, {target_table_key, old_key}, state);
5,592✔
115

2,796✔
116
    m_tree->erase(ndx);
5,592✔
117

2,796✔
118
    if (recurse) {
5,592✔
119
        _impl::TableFriend::remove_recursive(*origin_table, state); // Throws
×
120
    }
×
121
    if (old_key.is_unresolved()) {
5,592✔
122
        // We might have removed the last unresolved link - check it
6✔
123

6✔
124
        // FIXME: Exploit the fact that the values are sorted and unresolved
6✔
125
        // keys have a negative value.
6✔
126
        _impl::check_for_last_unresolved(m_tree.get());
12✔
127
    }
12✔
128
}
5,592✔
129

130
template <>
131
void Set<ObjKey>::do_clear()
132
{
924✔
133
    size_t ndx = size();
924✔
134
    while (ndx--) {
4,752✔
135
        do_erase(ndx);
3,828✔
136
    }
3,828✔
137

462✔
138
    m_tree->set_context_flag(false);
924✔
139
}
924✔
140

141
template <>
142
void Set<ObjLink>::do_insert(size_t ndx, ObjLink target_link)
UNCOV
143
{
×
NEW
144
    set_backlink(m_col_key, target_link);
×
UNCOV
145
    m_tree->insert(ndx, target_link);
×
UNCOV
146
}
×
147

148
template <>
149
void Set<ObjLink>::do_erase(size_t ndx)
UNCOV
150
{
×
UNCOV
151
    ObjLink old_link = get(ndx);
×
UNCOV
152
    CascadeState state(old_link.get_obj_key().is_unresolved() ? CascadeState::Mode::All : CascadeState::Mode::Strong);
×
153

NEW
154
    bool recurse = remove_backlink(m_col_key, old_link, state);
×
155

UNCOV
156
    m_tree->erase(ndx);
×
157

UNCOV
158
    if (recurse) {
×
NEW
159
        auto table = get_table_unchecked();
×
160
        _impl::TableFriend::remove_recursive(*table, state); // Throws
×
161
    }
×
UNCOV
162
}
×
163

164
template <>
165
void Set<Mixed>::do_insert(size_t ndx, Mixed value)
166
{
8,760✔
167
    REALM_ASSERT(!value.is_type(type_Link));
8,760✔
168
    if (value.is_type(type_TypedLink)) {
8,760✔
169
        auto target_link = value.get<ObjLink>();
4,422✔
170
        get_table_unchecked()->get_parent_group()->validate(target_link);
4,422✔
171
        set_backlink(m_col_key, target_link);
4,422✔
172
    }
4,422✔
173
    m_tree->insert(ndx, value);
8,760✔
174
}
8,760✔
175

176
template <>
177
void Set<Mixed>::do_erase(size_t ndx)
178
{
3,618✔
179
    if (Mixed old_value = get(ndx); old_value.is_type(type_TypedLink)) {
3,618✔
180
        auto old_link = old_value.get<ObjLink>();
1,032✔
181

516✔
182
        CascadeState state(old_link.get_obj_key().is_unresolved() ? CascadeState::Mode::All
516✔
183
                                                                  : CascadeState::Mode::Strong);
1,032✔
184
        bool recurse = remove_backlink(m_col_key, old_link, state);
1,032✔
185

516✔
186
        m_tree->erase(ndx);
1,032✔
187

516✔
188
        if (recurse) {
1,032✔
NEW
189
            auto table = get_table_unchecked();
×
190
            _impl::TableFriend::remove_recursive(*table, state); // Throws
×
191
        }
×
192
    }
1,032✔
193
    else {
2,586✔
194
        m_tree->erase(ndx);
2,586✔
195
    }
2,586✔
196
}
3,618✔
197

198
template <>
199
void Set<Mixed>::do_clear()
200
{
252✔
201
    size_t ndx = size();
252✔
202
    while (ndx--) {
2,292✔
203
        do_erase(ndx);
2,040✔
204
    }
2,040✔
205
}
252✔
206

207
template <>
208
void Set<Mixed>::migrate()
209
{
6✔
210
    // We should just move all string values to be before the binary values
3✔
211
    size_t first_binary = size();
6✔
212
    for (size_t n = 0; n < size(); n++) {
24✔
213
        if (m_tree->get(n).is_type(type_Binary)) {
24✔
214
            first_binary = n;
6✔
215
            break;
6✔
216
        }
6✔
217
    }
24✔
218

3✔
219
    for (size_t n = first_binary; n < size(); n++) {
36✔
220
        if (m_tree->get(n).is_type(type_String)) {
30✔
221
            m_tree->insert(first_binary, Mixed());
12✔
222
            m_tree->swap(n + 1, first_binary);
12✔
223
            m_tree->erase(n + 1);
12✔
224
            first_binary++;
12✔
225
        }
12✔
226
    }
30✔
227
}
6✔
228

229
template <class T>
230
void Set<T>::do_resort(size_t start, size_t end)
231
{
102✔
232
    if (end > size()) {
102✔
NEW
233
        end = size();
×
NEW
234
    }
×
235
    if (start >= end) {
102✔
236
        return;
6✔
237
    }
6✔
238
    std::vector<size_t> indices;
96✔
239
    indices.resize(end - start);
96✔
240
    std::iota(indices.begin(), indices.end(), 0);
96✔
241
    std::sort(indices.begin(), indices.end(), [&](auto a, auto b) {
1,272✔
242
        return get(a + start) < get(b + start);
1,272✔
243
    });
1,272✔
244
    for (size_t i = 0; i < indices.size(); ++i) {
594✔
245
        if (indices[i] != i) {
498✔
246
            m_tree->swap(i + start, start + indices[i]);
174✔
247
            auto it = std::find(indices.begin() + i, indices.end(), i);
174✔
248
            REALM_ASSERT(it != indices.end());
174✔
249
            *it = indices[i];
174✔
250
            indices[i] = i;
174✔
251
        }
174✔
252
    }
498✔
253
}
96✔
254

255
template <>
256
void Set<Mixed>::migration_resort()
257
{
12✔
258
    // sort order of strings and binaries changed
6✔
259
    auto first_string = std::lower_bound(begin(), end(), StringData(""));
12✔
260
    auto last_binary = std::partition_point(first_string, end(), [](const Mixed& item) {
48✔
261
        return item.is_type(type_String, type_Binary);
48✔
262
    });
48✔
263
    do_resort(first_string.index(), last_binary.index());
12✔
264
}
12✔
265

266
template <>
267
void Set<StringData>::migration_resort()
268
{
84✔
269
    // sort order of strings changed
42✔
270
    do_resort(0, size());
84✔
271
}
84✔
272

273
template <>
274
void Set<BinaryData>::migration_resort()
275
{
6✔
276
    // sort order of binaries changed
3✔
277
    do_resort(0, size());
6✔
278
}
6✔
279

280
void LnkSet::remove_target_row(size_t link_ndx)
281
{
×
282
    // Deleting the object will automatically remove all links
283
    // to it. So we do not have to manually remove the deleted link
284
    ObjKey k = get(link_ndx);
×
285
    get_target_table()->remove_object(k);
×
286
}
×
287

288
void LnkSet::remove_all_target_rows()
289
{
510✔
290
    if (m_set.update()) {
510✔
291
        _impl::TableFriend::batch_erase_rows(*get_target_table(), *m_set.m_tree);
510✔
292
    }
510✔
293
}
510✔
294

295
bool LnkSet::is_subset_of(const CollectionBase& rhs) const
296
{
24✔
297
    return this->m_set.is_subset_of(rhs);
24✔
298
}
24✔
299

300
bool LnkSet::is_strict_subset_of(const CollectionBase& rhs) const
301
{
×
302
    return this->m_set.is_strict_subset_of(rhs);
×
303
}
×
304

305
bool LnkSet::is_superset_of(const CollectionBase& rhs) const
306
{
×
307
    return this->m_set.is_superset_of(rhs);
×
308
}
×
309

310
bool LnkSet::is_strict_superset_of(const CollectionBase& rhs) const
311
{
×
312
    return this->m_set.is_strict_superset_of(rhs);
×
313
}
×
314

315
bool LnkSet::intersects(const CollectionBase& rhs) const
316
{
36✔
317
    return this->m_set.intersects(rhs);
36✔
318
}
36✔
319

320
bool LnkSet::set_equals(const CollectionBase& rhs) const
321
{
×
322
    return this->m_set.set_equals(rhs);
×
323
}
×
324

325
void LnkSet::to_json(std::ostream& out, size_t link_depth, JSONOutputMode output_mode,
326
                     util::FunctionRef<void(const Mixed&)> fn) const
327
{
72✔
328
    auto [open_str, close_str] = get_open_close_strings(link_depth, output_mode);
72✔
329

36✔
330
    out << open_str;
72✔
331
    out << "[";
72✔
332

36✔
333
    auto sz = m_set.size();
72✔
334
    for (size_t i = 0; i < sz; i++) {
144✔
335
        if (i > 0)
72✔
336
            out << ",";
24✔
337
        Mixed val(m_set.get(i));
72✔
338
        fn(val);
72✔
339
    }
72✔
340

36✔
341
    out << "]";
72✔
342
    out << close_str;
72✔
343
}
72✔
344

345

346
void set_sorted_indices(size_t sz, std::vector<size_t>& indices, bool ascending)
347
{
31,878✔
348
    indices.resize(sz);
31,878✔
349
    if (ascending) {
31,878✔
350
        std::iota(indices.begin(), indices.end(), 0);
31,626✔
351
    }
31,626✔
352
    else {
252✔
353
        std::iota(indices.rbegin(), indices.rend(), 0);
252✔
354
    }
252✔
355
}
31,878✔
356

357
template <typename Iterator>
358
static bool partition_points(const Set<Mixed>& set, std::vector<size_t>& indices, Iterator& first_string,
359
                             Iterator& first_binary, Iterator& end)
360
{
1,782✔
361
    first_string = std::partition_point(indices.begin(), indices.end(), [&](size_t i) {
1,734✔
362
        return set.get(i).is_type(type_Bool, type_Int, type_Float, type_Double, type_Decimal);
1,686✔
363
    });
1,686✔
364
    if (first_string == indices.end() || !set.get(*first_string).is_type(type_String))
1,782✔
365
        return false;
1,716✔
366
    first_binary = std::partition_point(first_string + 1, indices.end(), [&](size_t i) {
150✔
367
        return set.get(i).is_type(type_String);
150✔
368
    });
150✔
369
    if (first_binary == indices.end() || !set.get(*first_binary).is_type(type_Binary))
66✔
370
        return false;
30✔
371
    end = std::partition_point(first_binary + 1, indices.end(), [&](size_t i) {
72✔
372
        return set.get(i).is_type(type_Binary);
72✔
373
    });
72✔
374
    return true;
36✔
375
}
36✔
376

377
template <>
378
void Set<Mixed>::sort(std::vector<size_t>& indices, bool ascending) const
379
{
1,782✔
380
    set_sorted_indices(size(), indices, true);
1,782✔
381

891✔
382
    // The on-disk order is bool -> numbers -> string -> binary -> others
891✔
383
    // We want to merge the string and binary sections to match the sort order
891✔
384
    // of other collections. To do this we find the three partition points
891✔
385
    // where the first string occurs, the first binary occurs, and the first
891✔
386
    // non-binary after binaries occurs. If there's no strings or binaries we
891✔
387
    // don't have to do anything. If they're both non-empty, we perform an
891✔
388
    // in-place merge on the strings and binaries.
891✔
389
    std::vector<size_t>::iterator first_string, first_binary, end;
1,782✔
390
    if (partition_points(*this, indices, first_string, first_binary, end)) {
1,782✔
391
        std::inplace_merge(first_string, first_binary, end, [&](auto a, auto b) {
72✔
392
            return get(a) < get(b);
72✔
393
        });
72✔
394
    }
36✔
395
    if (!ascending) {
1,782✔
396
        std::reverse(indices.begin(), indices.end());
36✔
397
    }
36✔
398
}
1,782✔
399

400
} // 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