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

realm / realm-core / 1786

28 Oct 2023 12:35PM UTC coverage: 91.562% (-0.02%) from 91.582%
1786

push

Evergreen

web-flow
Improve configurations for sanitized builds (#6911)

* Refactor sanitizer flags for different build types:

** Enable address sanitizer for msvc
** Allow to build with sanitizer for diffent optimized build (also Debug)
** Make RelASAN, RelTSAN, RelUSAN, RelUSAN just shortcuts for half-optimized builds

* Fix usage of moved object for fuzz tester
* Check asan/tsan on macos x64/arm64
* Check asan with msvc 2019
* Remove Jenkins sanitized builders replaced by evergreen configs
* Work-around stack-use-after-scope with msvc2019 and mpark
* Fix crash on check with staled ColKeys
* fix a buffer overrun in a test
* fix a race in async_open_realm test util
* Add some logger related test fixes
* Work around catch2 limmitation with not thread safe asserts and TSAN races
* Run multiprocesses tests under sanitizers
* add assert for an error reported by undefined sanitizer
* Workaround uv scheduler main thread only constraint for callbacks called from non main thread and requesting a realm

---------

Co-authored-by: James Stone <james.stone@mongodb.com>

94310 of 173648 branches covered (0.0%)

54 of 63 new or added lines in 15 files covered. (85.71%)

2212 existing lines in 52 files now uncovered.

230602 of 251853 relevant lines covered (91.56%)

6943670.77 hits per line

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

83.46
/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
// FIXME: This method belongs in obj.cpp.
36
SetBasePtr Obj::get_setbase_ptr(ColKey col_key) const
37
{
116,907✔
38
    auto attr = get_table()->get_column_attr(col_key);
116,907✔
39
    REALM_ASSERT(attr.test(col_attr_Set));
116,907✔
40
    bool nullable = attr.test(col_attr_Nullable);
116,907✔
41

58,278✔
42
    switch (get_table()->get_column_type(col_key)) {
116,907✔
43
        case type_Int: {
7,947✔
44
            if (nullable)
7,947✔
45
                return std::make_unique<Set<util::Optional<Int>>>(*this, col_key);
3,663✔
46
            else
4,284✔
47
                return std::make_unique<Set<Int>>(*this, col_key);
4,284✔
UNCOV
48
        }
×
49
        case type_Bool: {
5,904✔
50
            if (nullable)
5,904✔
51
                return std::make_unique<Set<util::Optional<Bool>>>(*this, col_key);
3,108✔
52
            else
2,796✔
53
                return std::make_unique<Set<Bool>>(*this, col_key);
2,796✔
UNCOV
54
        }
×
55
        case type_Float: {
6,672✔
56
            if (nullable)
6,672✔
57
                return std::make_unique<Set<util::Optional<Float>>>(*this, col_key);
3,492✔
58
            else
3,180✔
59
                return std::make_unique<Set<Float>>(*this, col_key);
3,180✔
UNCOV
60
        }
×
61
        case type_Double: {
6,672✔
62
            if (nullable)
6,672✔
63
                return std::make_unique<Set<util::Optional<Double>>>(*this, col_key);
3,492✔
64
            else
3,180✔
65
                return std::make_unique<Set<Double>>(*this, col_key);
3,180✔
66
        }
×
67
        case type_String: {
6,672✔
68
            return std::make_unique<Set<String>>(*this, col_key);
6,672✔
UNCOV
69
        }
×
70
        case type_Binary: {
6,528✔
71
            return std::make_unique<Set<Binary>>(*this, col_key);
6,528✔
UNCOV
72
        }
×
73
        case type_Timestamp: {
6,600✔
74
            return std::make_unique<Set<Timestamp>>(*this, col_key);
6,600✔
UNCOV
75
        }
×
76
        case type_Decimal: {
6,198✔
77
            return std::make_unique<Set<Decimal128>>(*this, col_key);
6,198✔
UNCOV
78
        }
×
79
        case type_ObjectId: {
14,772✔
80
            if (nullable)
14,772✔
81
                return std::make_unique<Set<util::Optional<ObjectId>>>(*this, col_key);
11,976✔
82
            else
2,796✔
83
                return std::make_unique<Set<ObjectId>>(*this, col_key);
2,796✔
84
        }
×
85
        case type_UUID: {
5,904✔
86
            if (nullable)
5,904✔
87
                return std::make_unique<Set<util::Optional<UUID>>>(*this, col_key);
3,108✔
88
            else
2,796✔
89
                return std::make_unique<Set<UUID>>(*this, col_key);
2,796✔
UNCOV
90
        }
×
UNCOV
91
        case type_TypedLink: {
✔
UNCOV
92
            return std::make_unique<Set<ObjLink>>(*this, col_key);
×
UNCOV
93
        }
×
94
        case type_Mixed: {
10,146✔
95
            return std::make_unique<Set<Mixed>>(*this, col_key);
10,146✔
UNCOV
96
        }
×
97
        case type_Link: {
32,892✔
98
            return std::make_unique<LnkSet>(*this, col_key);
32,892✔
UNCOV
99
        }
×
UNCOV
100
        case type_LinkList:
✔
UNCOV
101
            break;
×
UNCOV
102
    }
×
UNCOV
103
    REALM_TERMINATE("Unsupported column type.");
×
UNCOV
104
}
×
105

106
void SetBase::insert_repl(Replication* repl, size_t index, Mixed value) const
107
{
60,492✔
108
    repl->set_insert(*this, index, value);
60,492✔
109
}
60,492✔
110

111
void SetBase::erase_repl(Replication* repl, size_t index, Mixed value) const
112
{
9,456✔
113
    repl->set_erase(*this, index, value);
9,456✔
114
}
9,456✔
115

116
void SetBase::clear_repl(Replication* repl) const
117
{
2,412✔
118
    repl->set_clear(*this);
2,412✔
119
}
2,412✔
120

121
std::vector<Mixed> SetBase::convert_to_mixed_set(const CollectionBase& rhs)
122
{
522✔
123
    std::vector<Mixed> mixed;
522✔
124
    mixed.reserve(rhs.size());
522✔
125
    for (size_t i = 0; i < rhs.size(); i++) {
2,754✔
126
        mixed.push_back(rhs.get_any(i));
2,232✔
127
    }
2,232✔
128
    std::sort(mixed.begin(), mixed.end(), SetElementLessThan<Mixed>());
522✔
129
    mixed.erase(std::unique(mixed.begin(), mixed.end()), mixed.end());
522✔
130
    return mixed;
522✔
131
}
522✔
132

133
template <>
134
void Set<ObjKey>::do_insert(size_t ndx, ObjKey target_key)
135
{
16,938✔
136
    auto origin_table = m_obj.get_table();
16,938✔
137
    auto target_table_key = origin_table->get_opposite_table_key(m_col_key);
16,938✔
138
    m_obj.set_backlink(m_col_key, {target_table_key, target_key});
16,938✔
139
    m_tree->insert(ndx, target_key);
16,938✔
140
    if (target_key.is_unresolved()) {
16,938✔
141
        m_tree->set_context_flag(true);
54✔
142
    }
54✔
143
}
16,938✔
144

145
template <>
146
void Set<ObjKey>::do_erase(size_t ndx)
147
{
5,592✔
148
    auto origin_table = m_obj.get_table();
5,592✔
149
    auto target_table_key = origin_table->get_opposite_table_key(m_col_key);
5,592✔
150
    ObjKey old_key = get(ndx);
5,592✔
151
    CascadeState state(old_key.is_unresolved() ? CascadeState::Mode::All : CascadeState::Mode::Strong);
5,586✔
152

2,796✔
153
    bool recurse = m_obj.remove_backlink(m_col_key, {target_table_key, old_key}, state);
5,592✔
154

2,796✔
155
    m_tree->erase(ndx);
5,592✔
156

2,796✔
157
    if (recurse) {
5,592✔
UNCOV
158
        _impl::TableFriend::remove_recursive(*origin_table, state); // Throws
×
UNCOV
159
    }
×
160
    if (old_key.is_unresolved()) {
5,592✔
161
        // We might have removed the last unresolved link - check it
6✔
162

6✔
163
        // FIXME: Exploit the fact that the values are sorted and unresolved
6✔
164
        // keys have a negative value.
6✔
165
        _impl::check_for_last_unresolved(m_tree.get());
12✔
166
    }
12✔
167
}
5,592✔
168

169
template <>
170
void Set<ObjKey>::do_clear()
171
{
924✔
172
    size_t ndx = size();
924✔
173
    while (ndx--) {
4,752✔
174
        do_erase(ndx);
3,828✔
175
    }
3,828✔
176

462✔
177
    m_tree->set_context_flag(false);
924✔
178
}
924✔
179

180
template <>
181
void Set<ObjLink>::do_insert(size_t ndx, ObjLink target_link)
182
{
30✔
183
    m_obj.set_backlink(m_col_key, target_link);
30✔
184
    m_tree->insert(ndx, target_link);
30✔
185
}
30✔
186

187
template <>
188
void Set<ObjLink>::do_erase(size_t ndx)
189
{
6✔
190
    ObjLink old_link = get(ndx);
6✔
191
    CascadeState state(old_link.get_obj_key().is_unresolved() ? CascadeState::Mode::All : CascadeState::Mode::Strong);
6✔
192

3✔
193
    bool recurse = m_obj.remove_backlink(m_col_key, old_link, state);
6✔
194

3✔
195
    m_tree->erase(ndx);
6✔
196

3✔
197
    if (recurse) {
6✔
UNCOV
198
        auto table = m_obj.get_table();
×
UNCOV
199
        _impl::TableFriend::remove_recursive(*table, state); // Throws
×
UNCOV
200
    }
×
201
}
6✔
202

203
template <>
204
void Set<Mixed>::do_insert(size_t ndx, Mixed value)
205
{
8,478✔
206
    if (value.is_type(type_TypedLink)) {
8,478✔
207
        auto target_link = value.get<ObjLink>();
4,416✔
208
        m_obj.get_table()->get_parent_group()->validate(target_link);
4,416✔
209
        m_obj.set_backlink(m_col_key, target_link);
4,416✔
210
    }
4,416✔
211
    m_tree->insert(ndx, value);
8,478✔
212
}
8,478✔
213

214
template <>
215
void Set<Mixed>::do_erase(size_t ndx)
216
{
3,618✔
217
    if (Mixed old_value = get(ndx); old_value.is_type(type_TypedLink)) {
3,618✔
218
        auto old_link = old_value.get<ObjLink>();
1,032✔
219

516✔
220
        CascadeState state(old_link.get_obj_key().is_unresolved() ? CascadeState::Mode::All
516✔
221
                                                                  : CascadeState::Mode::Strong);
1,032✔
222
        bool recurse = m_obj.remove_backlink(m_col_key, old_link, state);
1,032✔
223

516✔
224
        m_tree->erase(ndx);
1,032✔
225

516✔
226
        if (recurse) {
1,032✔
UNCOV
227
            auto table = m_obj.get_table();
×
UNCOV
228
            _impl::TableFriend::remove_recursive(*table, state); // Throws
×
UNCOV
229
        }
×
230
    }
1,032✔
231
    else {
2,586✔
232
        m_tree->erase(ndx);
2,586✔
233
    }
2,586✔
234
}
3,618✔
235

236
template <>
237
void Set<Mixed>::do_clear()
238
{
252✔
239
    size_t ndx = size();
252✔
240
    while (ndx--) {
2,292✔
241
        do_erase(ndx);
2,040✔
242
    }
2,040✔
243
}
252✔
244

245
template <>
246
void Set<Mixed>::migrate()
247
{
6✔
248
    // We should just move all string values to be before the binary values
3✔
249
    size_t first_binary = size();
6✔
250
    for (size_t n = 0; n < size(); n++) {
24✔
251
        if (m_tree->get(n).is_type(type_Binary)) {
24✔
252
            first_binary = n;
6✔
253
            break;
6✔
254
        }
6✔
255
    }
24✔
256

3✔
257
    for (size_t n = first_binary; n < size(); n++) {
36✔
258
        if (m_tree->get(n).is_type(type_String)) {
30✔
259
            m_tree->insert(first_binary, Mixed());
12✔
260
            m_tree->swap(n + 1, first_binary);
12✔
261
            m_tree->erase(n + 1);
12✔
262
            first_binary++;
12✔
263
        }
12✔
264
    }
30✔
265
}
6✔
266

267
void LnkSet::remove_target_row(size_t link_ndx)
UNCOV
268
{
×
269
    // Deleting the object will automatically remove all links
270
    // to it. So we do not have to manually remove the deleted link
UNCOV
271
    ObjKey k = get(link_ndx);
×
UNCOV
272
    get_target_table()->remove_object(k);
×
UNCOV
273
}
×
274

275
void LnkSet::remove_all_target_rows()
276
{
510✔
277
    if (m_set.update()) {
510✔
278
        _impl::TableFriend::batch_erase_rows(*get_target_table(), *m_set.m_tree);
510✔
279
    }
510✔
280
}
510✔
281

282
bool LnkSet::is_subset_of(const CollectionBase& rhs) const
283
{
24✔
284
    return this->m_set.is_subset_of(rhs);
24✔
285
}
24✔
286

287
bool LnkSet::is_strict_subset_of(const CollectionBase& rhs) const
UNCOV
288
{
×
UNCOV
289
    return this->m_set.is_strict_subset_of(rhs);
×
UNCOV
290
}
×
291

292
bool LnkSet::is_superset_of(const CollectionBase& rhs) const
UNCOV
293
{
×
UNCOV
294
    return this->m_set.is_superset_of(rhs);
×
UNCOV
295
}
×
296

297
bool LnkSet::is_strict_superset_of(const CollectionBase& rhs) const
UNCOV
298
{
×
UNCOV
299
    return this->m_set.is_strict_superset_of(rhs);
×
UNCOV
300
}
×
301

302
bool LnkSet::intersects(const CollectionBase& rhs) const
303
{
36✔
304
    return this->m_set.intersects(rhs);
36✔
305
}
36✔
306

307
bool LnkSet::set_equals(const CollectionBase& rhs) const
UNCOV
308
{
×
UNCOV
309
    return this->m_set.set_equals(rhs);
×
UNCOV
310
}
×
311

312
void set_sorted_indices(size_t sz, std::vector<size_t>& indices, bool ascending)
313
{
31,878✔
314
    indices.resize(sz);
31,878✔
315
    if (ascending) {
31,878✔
316
        std::iota(indices.begin(), indices.end(), 0);
31,626✔
317
    }
31,626✔
318
    else {
252✔
319
        std::iota(indices.rbegin(), indices.rend(), 0);
252✔
320
    }
252✔
321
}
31,878✔
322

323
template <typename Iterator>
324
static bool partition_points(const Set<Mixed>& set, std::vector<size_t>& indices, Iterator& first_string,
325
                             Iterator& first_binary, Iterator& end)
326
{
1,782✔
327
    first_string = std::partition_point(indices.begin(), indices.end(), [&](size_t i) {
1,734✔
328
        return set.get(i).is_type(type_Bool, type_Int, type_Float, type_Double, type_Decimal);
1,686✔
329
    });
1,686✔
330
    if (first_string == indices.end() || !set.get(*first_string).is_type(type_String))
1,782✔
331
        return false;
1,716✔
332
    first_binary = std::partition_point(first_string + 1, indices.end(), [&](size_t i) {
150✔
333
        return set.get(i).is_type(type_String);
150✔
334
    });
150✔
335
    if (first_binary == indices.end() || !set.get(*first_binary).is_type(type_Binary))
66✔
336
        return false;
30✔
337
    end = std::partition_point(first_binary + 1, indices.end(), [&](size_t i) {
72✔
338
        return set.get(i).is_type(type_Binary);
72✔
339
    });
72✔
340
    return true;
36✔
341
}
36✔
342

343
template <>
344
void Set<Mixed>::sort(std::vector<size_t>& indices, bool ascending) const
345
{
1,782✔
346
    set_sorted_indices(size(), indices, true);
1,782✔
347

891✔
348
    // The on-disk order is bool -> numbers -> string -> binary -> others
891✔
349
    // We want to merge the string and binary sections to match the sort order
891✔
350
    // of other collections. To do this we find the three partition points
891✔
351
    // where the first string occurs, the first binary occurs, and the first
891✔
352
    // non-binary after binaries occurs. If there's no strings or binaries we
891✔
353
    // don't have to do anything. If they're both non-empty, we perform an
891✔
354
    // in-place merge on the strings and binaries.
891✔
355
    std::vector<size_t>::iterator first_string, first_binary, end;
1,782✔
356
    if (partition_points(*this, indices, first_string, first_binary, end)) {
1,782✔
357
        std::inplace_merge(first_string, first_binary, end, [&](auto a, auto b) {
120✔
358
            return get(a) < get(b);
120✔
359
        });
120✔
360
    }
36✔
361
    if (!ascending) {
1,782✔
362
        std::reverse(indices.begin(), indices.end());
36✔
363
    }
36✔
364
}
1,782✔
365

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