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

realm / realm-core / thomas.goyne_120

20 Nov 2023 07:46PM UTC coverage: 92.066% (+0.4%) from 91.699%
thomas.goyne_120

push

Evergreen

web-flow
Fix client reset cycle detection for PBS recovery errors (#7149)

Tracking that a client reset was in progress was done in the same write
transaction as the recovery operation, so if recovery failed the tracking was
rolled back too. This worked for FLX due to that codepath committing before
beginning recovery.

85328 of 169122 branches covered (0.0%)

30 of 30 new or added lines in 3 files covered. (100.0%)

169 existing lines in 18 files now uncovered.

233147 of 253239 relevant lines covered (92.07%)

5776222.82 hits per line

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

87.01
/src/realm/query_expression.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/query_expression.hpp>
20
#include <realm/group.hpp>
21
#include <realm/dictionary.hpp>
22

23
namespace realm {
24

25
void LinkMap::set_base_table(ConstTableRef table)
26
{
846,708✔
27
    if (table == get_base_table())
846,708✔
28
        return;
38,268✔
29

404,220✔
30
    m_tables.clear();
808,440✔
31
    m_tables.push_back(table);
808,440✔
32
    m_link_types.clear();
808,440✔
33
    m_only_unary_links = true;
808,440✔
34

404,220✔
35
    for (size_t i = 0; i < m_link_column_keys.size(); i++) {
840,639✔
36
        ColKey link_column_key = m_link_column_keys[i];
32,199✔
37
        // Link column can be either LinkList or single Link
16,098✔
38
        ColumnType type = link_column_key.get_type();
32,199✔
39
        REALM_ASSERT(Table::is_link_type(type) || type == col_type_BackLink);
32,199✔
40
        if (type == col_type_LinkList || type == col_type_BackLink ||
32,199✔
41
            (type == col_type_Link && link_column_key.is_collection())) {
26,448✔
42
            m_only_unary_links = false;
12,090✔
43
        }
12,090✔
44

16,098✔
45
        m_link_types.push_back(type);
32,199✔
46
        REALM_ASSERT(table->valid_column(link_column_key));
32,199✔
47
        table = table.unchecked_ptr()->get_opposite_table(link_column_key);
32,199✔
48
        m_tables.push_back(table);
32,199✔
49
    }
32,199✔
50
}
808,440✔
51

52
void LinkMap::collect_dependencies(std::vector<TableKey>& tables) const
53
{
3,210✔
54
    for (auto& t : m_tables) {
5,412✔
55
        TableKey k = t->get_key();
5,412✔
56
        if (find(tables.begin(), tables.end(), k) == tables.end()) {
5,412✔
57
            tables.push_back(k);
1,986✔
58
        }
1,986✔
59
    }
5,412✔
60
}
3,210✔
61

62
std::string LinkMap::description(util::serializer::SerialisationState& state) const
63
{
7,560✔
64
    std::string s;
7,560✔
65
    for (size_t i = 0; i < m_link_column_keys.size(); ++i) {
17,280✔
66
        if (i < m_tables.size() && m_tables[i]) {
9,720✔
67
            s += state.get_column_name(m_tables[i], m_link_column_keys[i]);
9,720✔
68
            if (i != m_link_column_keys.size() - 1) {
9,720✔
69
                s += util::serializer::value_separator;
2,160✔
70
            }
2,160✔
71
        }
9,720✔
72
    }
9,720✔
73
    return s;
7,560✔
74
}
7,560✔
75

76
bool LinkMap::map_links(size_t column, ObjKey key, LinkMapFunction lm) const
77
{
490,791✔
78
    if (!key || key.is_unresolved())
490,791✔
79
        return true;
7,398✔
80
    if (column == m_link_column_keys.size()) {
483,393✔
81
        return lm(key);
450,684✔
82
    }
450,684✔
83

16,350✔
84
    ColumnType type = m_link_types[column];
32,709✔
85
    ColKey column_key = m_link_column_keys[column];
32,709✔
86
    const Obj obj = m_tables[column]->get_object(key);
32,709✔
87
    if (column_key.is_collection()) {
32,709✔
88
        auto coll = obj.get_linkcollection_ptr(column_key);
2,412✔
89
        size_t sz = coll->size();
2,412✔
90
        for (size_t t = 0; t < sz; t++) {
6,252✔
91
            if (!map_links(column + 1, coll->get_key(t), lm))
3,840✔
92
                return false;
×
93
        }
3,840✔
94
    }
2,412✔
95
    else if (type == col_type_Link) {
30,297✔
96
        return map_links(column + 1, obj.get<ObjKey>(column_key), lm);
29,631✔
97
    }
29,631✔
98
    else if (type == col_type_BackLink) {
666✔
99
        auto backlinks = obj.get_all_backlinks(column_key);
654✔
100
        for (auto k : backlinks) {
651✔
101
            if (!map_links(column + 1, k, lm))
648✔
102
                return false;
×
103
        }
648✔
104
    }
654✔
105
    else {
12✔
106
        REALM_TERMINATE("Invalid column type in LinkMap::map_links()");
107
    }
12✔
108
    return true;
17,883✔
109
}
32,709✔
110

111
void LinkMap::map_links(size_t column, size_t row, LinkMapFunction lm) const
112
{
355,233✔
113
    ColumnType type = m_link_types[column];
355,233✔
114
    ColKey column_key = m_link_column_keys[column];
355,233✔
115
    if (type == col_type_Link && !column_key.is_set()) {
355,233✔
116
        if (column_key.is_dictionary()) {
155,283✔
117
            auto& leaf = mpark::get<ArrayInteger>(m_leaf);
936✔
118
            if (leaf.get(row)) {
936✔
119
                Allocator& alloc = get_base_table()->get_alloc();
702✔
120
                Array top(alloc);
702✔
121
                top.set_parent(const_cast<ArrayInteger*>(&leaf), row);
702✔
122
                top.init_from_parent();
702✔
123
                BPlusTree<Mixed> values(alloc);
702✔
124
                values.set_parent(&top, 1);
702✔
125
                values.init_from_parent();
702✔
126

351✔
127
                // Iterate through values and insert all link values
351✔
128
                values.for_all([&](Mixed m) {
1,386✔
129
                    if (m.is_type(type_TypedLink)) {
1,386✔
130
                        auto link = m.get_link();
1,368✔
131
                        REALM_ASSERT(link.get_table_key() == this->m_tables[column + 1]->get_key());
1,368✔
132
                        if (!map_links(column + 1, link.get_obj_key(), lm))
1,368✔
133
                            return false;
×
134
                    }
1,386✔
135
                    return true;
1,386✔
136
                });
1,386✔
137
            }
702✔
138
        }
936✔
139
        else {
154,347✔
140
            REALM_ASSERT(!column_key.is_collection());
154,347✔
141
            map_links(column + 1, mpark::get<ArrayKey>(m_leaf).get(row), lm);
154,347✔
142
        }
154,347✔
143
    }
155,283✔
144
    else if (type == col_type_LinkList || (type == col_type_Link && column_key.is_set())) {
199,950✔
145
        ref_type ref;
195,582✔
146
        if (auto list = mpark::get_if<ArrayList>(&m_leaf)) {
195,582✔
147
            ref = list->get(row);
194,676✔
148
        }
194,676✔
149
        else {
906✔
150
            ref = mpark::get<ArrayKey>(m_leaf).get_as_ref(row);
906✔
151
        }
906✔
152

97,791✔
153
        if (ref) {
195,582✔
154
            BPlusTree<ObjKey> links(get_base_table()->get_alloc());
138,924✔
155
            links.init_from_ref(ref);
138,924✔
156
            size_t sz = links.size();
138,924✔
157
            for (size_t t = 0; t < sz; t++) {
411,519✔
158
                if (!map_links(column + 1, links.get(t), lm))
272,901✔
159
                    break;
306✔
160
            }
272,901✔
161
        }
138,924✔
162
    }
195,582✔
163
    else if (type == col_type_BackLink) {
4,368✔
164
        auto& back_links = mpark::get<ArrayBacklink>(m_leaf);
4,368✔
165
        size_t sz = back_links.get_backlink_count(row);
4,368✔
166
        for (size_t t = 0; t < sz; t++) {
32,412✔
167
            ObjKey k = back_links.get_backlink(row, t);
28,122✔
168
            if (!map_links(column + 1, k, lm))
28,122✔
169
                break;
78✔
170
        }
28,122✔
171
    }
4,368✔
UNCOV
172
    else {
×
UNCOV
173
        REALM_ASSERT(false);
×
UNCOV
174
    }
×
175
}
355,233✔
176

177
std::vector<ObjKey> LinkMap::get_origin_ndxs(ObjKey key, size_t column) const
178
{
19,302✔
179
    if (column == m_link_types.size()) {
19,302✔
180
        return {key};
15,162✔
181
    }
15,162✔
182
    std::vector<ObjKey> keys = get_origin_ndxs(key, column + 1);
4,140✔
183
    std::vector<ObjKey> ret;
4,140✔
184
    auto origin_col = m_link_column_keys[column];
4,140✔
185
    auto origin = m_tables[column];
4,140✔
186
    auto link_type = m_link_types[column];
4,140✔
187
    if (link_type == col_type_BackLink) {
4,140✔
188
        auto link_table = origin->get_opposite_table(origin_col);
660✔
189
        ColKey link_col_key = origin->get_opposite_column(origin_col);
660✔
190

330✔
191
        for (auto k : keys) {
660✔
192
            const Obj o = link_table.unchecked_ptr()->get_object(k);
660✔
193
            if (link_col_key.is_collection()) {
660✔
194
                auto coll = o.get_linkcollection_ptr(link_col_key);
48✔
195
                auto sz = coll->size();
48✔
196
                for (size_t i = 0; i < sz; i++) {
144✔
197
                    if (ObjKey x = coll->get_key(i))
96✔
198
                        ret.push_back(x);
84✔
199
                }
96✔
200
            }
48✔
201
            else if (link_col_key.get_type() == col_type_Link) {
612✔
202
                ret.push_back(o.get<ObjKey>(link_col_key));
612✔
203
            }
612✔
204
        }
660✔
205
    }
660✔
206
    else {
3,480✔
207
        auto target = m_tables[column + 1];
3,480✔
208
        for (auto k : keys) {
3,594✔
209
            const Obj o = target->get_object(k);
3,594✔
210
            auto cnt = o.get_backlink_count(*origin, origin_col);
3,594✔
211
            for (size_t i = 0; i < cnt; i++) {
7,878✔
212
                ret.push_back(o.get_backlink(*origin, origin_col, i));
4,284✔
213
            }
4,284✔
214
        }
3,594✔
215
    }
3,480✔
216
    return ret;
4,140✔
217
}
4,140✔
218

219
ColumnDictionaryKey Columns<Dictionary>::key(const Mixed& key_value)
220
{
762✔
221
    if (m_key_type != type_Mixed && key_value.get_type() != m_key_type) {
762✔
222
        throw InvalidArgument(ErrorCodes::TypeMismatch, util::format("Key not a %1", m_key_type));
×
223
    }
×
224

381✔
225
    return ColumnDictionaryKey(key_value, *this);
762✔
226
}
762✔
227

228
ColumnDictionaryKeys Columns<Dictionary>::keys()
229
{
12✔
230
    return ColumnDictionaryKeys(*this);
12✔
231
}
12✔
232

233
void ColumnDictionaryKey::init_key(Mixed key_value)
234
{
1,692✔
235
    REALM_ASSERT(!key_value.is_null());
1,692✔
236

846✔
237
    m_key = key_value;
1,692✔
238
    m_key.use_buffer(m_buffer);
1,692✔
239
}
1,692✔
240

241
void ColumnDictionaryKeys::set_cluster(const Cluster* cluster)
242
{
78✔
243
    if (m_link_map.has_links()) {
78✔
244
        m_link_map.set_cluster(cluster);
6✔
245
    }
6✔
246
    else {
72✔
247
        m_leaf.emplace(m_link_map.get_base_table()->get_alloc());
72✔
248
        cluster->init_leaf(m_column_key, &*m_leaf);
72✔
249
    }
72✔
250
}
78✔
251

252

253
void ColumnDictionaryKeys::evaluate(size_t index, ValueBase& destination)
254
{
6,666✔
255
    if (m_link_map.has_links()) {
6,666✔
256
        REALM_ASSERT(!m_leaf);
60✔
257
        std::vector<ObjKey> links = m_link_map.get_links(index);
60✔
258
        auto sz = links.size();
60✔
259

30✔
260
        // Here we don't really know how many values to expect
30✔
261
        std::vector<Mixed> values;
60✔
262
        for (size_t t = 0; t < sz; t++) {
120✔
263
            const Obj obj = m_link_map.get_target_table()->get_object(links[t]);
60✔
264
            auto dict = obj.get_dictionary(m_column_key);
60✔
265
            // Insert all values
30✔
266
            dict.for_all_keys<StringData>([&values](const Mixed& value) {
150✔
267
                values.emplace_back(value);
150✔
268
            });
150✔
269
        }
60✔
270

30✔
271
        // Copy values over
30✔
272
        destination.init(true, values.size());
60✔
273
        destination.set(values.begin(), values.end());
60✔
274
    }
60✔
275
    else {
6,606✔
276
        // Not a link column
3,303✔
277
        Allocator& alloc = get_base_table()->get_alloc();
6,606✔
278

3,303✔
279
        REALM_ASSERT(m_leaf);
6,606✔
280
        if (m_leaf->get(index)) {
6,606✔
281
            Array top(alloc);
6,606✔
282
            top.set_parent(&*m_leaf, index);
6,606✔
283
            top.init_from_parent();
6,606✔
284
            BPlusTree<StringData> keys(alloc);
6,606✔
285
            keys.set_parent(&top, 0);
6,606✔
286
            keys.init_from_parent();
6,606✔
287

3,303✔
288
            destination.init(true, keys.size());
6,606✔
289
            size_t n = 0;
6,606✔
290
            // Iterate through BPlusTree and insert all keys
3,303✔
291
            keys.for_all([&](StringData str) {
13,014✔
292
                destination.set(n, str);
13,014✔
293
                n++;
13,014✔
294
            });
13,014✔
295
        }
6,606✔
296
    }
6,606✔
297
}
6,666✔
298

299
void ColumnDictionaryKey::evaluate(size_t index, ValueBase& destination)
300
{
10,176✔
301
    if (links_exist()) {
10,176✔
302
        REALM_ASSERT(!m_leaf);
540✔
303
        std::vector<ObjKey> links = m_link_map.get_links(index);
540✔
304
        auto sz = links.size();
540✔
305

270✔
306
        destination.init_for_links(m_link_map.only_unary_links(), sz);
540✔
307
        for (size_t t = 0; t < sz; t++) {
4,320✔
308
            const Obj obj = m_link_map.get_target_table()->get_object(links[t]);
3,780✔
309
            auto dict = obj.get_dictionary(m_column_key);
3,780✔
310
            Mixed val;
3,780✔
311
            if (auto opt_val = dict.try_get(m_key)) {
3,780✔
312
                val = *opt_val;
3,144✔
313
                if (m_prop_list.size()) {
3,144✔
314
                    if (val.is_type(type_TypedLink)) {
×
315
                        auto obj = get_base_table()->get_parent_group()->get_object(val.get<ObjLink>());
×
316
                        val = obj.get_any(m_prop_list.begin(), m_prop_list.end());
×
317
                    }
×
318
                    else {
×
319
                        val = {};
×
320
                    }
×
321
                }
×
322
            }
3,144✔
323
            destination.set(t, val);
3,780✔
324
        }
3,780✔
325
    }
540✔
326
    else {
9,636✔
327
        // Not a link column
4,818✔
328
        Allocator& alloc = get_base_table()->get_alloc();
9,636✔
329

4,818✔
330
        REALM_ASSERT(m_leaf);
9,636✔
331
        if (m_leaf->get(index)) {
9,636✔
332
            Array top(alloc);
9,636✔
333
            top.set_parent(&*m_leaf, index);
9,636✔
334
            top.init_from_parent();
9,636✔
335
            BPlusTree<StringData> keys(alloc);
9,636✔
336
            keys.set_parent(&top, 0);
9,636✔
337
            keys.init_from_parent();
9,636✔
338

4,818✔
339
            Mixed val;
9,636✔
340
            size_t ndx = keys.find_first(m_key.get_string());
9,636✔
341
            if (ndx != realm::npos) {
9,636✔
342
                BPlusTree<Mixed> values(alloc);
6,750✔
343
                values.set_parent(&top, 1);
6,750✔
344
                values.init_from_parent();
6,750✔
345
                val = values.get(ndx);
6,750✔
346
                if (m_prop_list.size()) {
6,750✔
347
                    if (val.is_type(type_TypedLink)) {
36✔
348
                        auto obj = get_base_table()->get_parent_group()->get_object(val.get<ObjLink>());
36✔
349
                        val = obj.get_any(m_prop_list.begin(), m_prop_list.end());
36✔
350
                    }
36✔
351
                    else {
×
352
                        val = {};
×
353
                    }
×
354
                }
36✔
355
            }
6,750✔
356
            destination.set(0, val);
9,636✔
357
        }
9,636✔
358
    }
9,636✔
359
}
10,176✔
360

361
class DictionarySize : public Columns<Dictionary> {
362
public:
363
    DictionarySize(const Columns<Dictionary>& other)
364
        : Columns<Dictionary>(other)
365
    {
12✔
366
    }
12✔
367
    void evaluate(size_t index, ValueBase& destination) override
368
    {
1,200✔
369
        Allocator& alloc = this->m_link_map.get_target_table()->get_alloc();
1,200✔
370
        Value<int64_t> list_refs;
1,200✔
371
        this->get_lists(index, list_refs, 1);
1,200✔
372
        destination.init(list_refs.m_from_list, list_refs.size());
1,200✔
373
        for (size_t i = 0; i < list_refs.size(); i++) {
2,400✔
374
            ref_type ref = to_ref(list_refs[i].get_int());
1,200✔
375
            size_t s = _impl::get_collection_size_from_ref(ref, alloc);
1,200✔
376
            destination.set(i, int64_t(s));
1,200✔
377
        }
1,200✔
378
    }
1,200✔
379

380
    std::unique_ptr<Subexpr> clone() const override
381
    {
24✔
382
        return std::unique_ptr<Subexpr>(new DictionarySize(*this));
24✔
383
    }
24✔
384
};
385

386
SizeOperator<int64_t> Columns<Dictionary>::size()
387
{
12✔
388
    std::unique_ptr<Subexpr> ptr(new DictionarySize(*this));
12✔
389
    return SizeOperator<int64_t>(std::move(ptr));
12✔
390
}
12✔
391

392
void Columns<Dictionary>::evaluate(size_t index, ValueBase& destination)
393
{
6,786✔
394
    if (links_exist()) {
6,786✔
395
        REALM_ASSERT(!m_leaf);
180✔
396
        std::vector<ObjKey> links = m_link_map.get_links(index);
180✔
397
        auto sz = links.size();
180✔
398

90✔
399
        // Here we don't really know how many values to expect
90✔
400
        std::vector<Mixed> values;
180✔
401
        for (size_t t = 0; t < sz; t++) {
1,980✔
402
            const Obj obj = m_link_map.get_target_table()->get_object(links[t]);
1,800✔
403
            auto dict = obj.get_dictionary(m_column_key);
1,800✔
404
            // Insert all values
900✔
405
            dict.for_all_values([&values](const Mixed& value) {
3,552✔
406
                values.emplace_back(value);
3,552✔
407
            });
3,552✔
408
        }
1,800✔
409

90✔
410
        // Copy values over
90✔
411
        destination.init(true, values.size());
180✔
412
        destination.set(values.begin(), values.end());
180✔
413
    }
180✔
414
    else {
6,606✔
415
        // Not a link column
3,303✔
416
        Allocator& alloc = get_base_table()->get_alloc();
6,606✔
417

3,303✔
418
        REALM_ASSERT(m_leaf);
6,606✔
419
        if (m_leaf->get(index)) {
6,606✔
420
            Array top(alloc);
6,606✔
421
            top.set_parent(&*m_leaf, index);
6,606✔
422
            top.init_from_parent();
6,606✔
423
            BPlusTree<Mixed> values(alloc);
6,606✔
424
            values.set_parent(&top, 1);
6,606✔
425
            values.init_from_parent();
6,606✔
426

3,303✔
427
            destination.init(true, values.size());
6,606✔
428
            size_t n = 0;
6,606✔
429
            // Iterate through BPlusTreee and insert all values
3,303✔
430
            values.for_all([&](Mixed val) {
13,026✔
431
                destination.set(n, val);
13,026✔
432
                n++;
13,026✔
433
            });
13,026✔
434
        }
6,606✔
435
    }
6,606✔
436
}
6,786✔
437

438

439
void Columns<Link>::evaluate(size_t index, ValueBase& destination)
440
{
2,070✔
441
    // Destination must be of Key type. It only makes sense to
1,035✔
442
    // compare keys with keys
1,035✔
443
    std::vector<ObjKey> links = m_link_map.get_links(index);
2,070✔
444

1,035✔
445
    if (m_link_map.only_unary_links()) {
2,070✔
446
        ObjKey key;
1,242✔
447
        if (!links.empty()) {
1,242✔
448
            key = links[0];
1,038✔
449
        }
1,038✔
450
        destination.init(false, 1);
1,242✔
451
        destination.set(0, key);
1,242✔
452
    }
1,242✔
453
    else {
828✔
454
        destination.init(true, links.size());
828✔
455
        destination.set(links.begin(), links.end());
828✔
456
    }
828✔
457
}
2,070✔
458

459
void ColumnListBase::set_cluster(const Cluster* cluster)
460
{
38,697✔
461
    if (m_link_map.has_links()) {
38,697✔
462
        m_link_map.set_cluster(cluster);
8,013✔
463
    }
8,013✔
464
    else {
30,684✔
465
        m_leaf.emplace(m_link_map.get_base_table()->get_alloc());
30,684✔
466
        cluster->init_leaf(m_column_key, &*m_leaf);
30,684✔
467
    }
30,684✔
468
}
38,697✔
469

470
void ColumnListBase::get_lists(size_t index, Value<int64_t>& destination, size_t nb_elements)
471
{
2,015,670✔
472
    if (m_link_map.has_links()) {
2,015,670✔
473
        std::vector<ObjKey> links = m_link_map.get_links(index);
56,982✔
474
        auto sz = links.size();
56,982✔
475

28,482✔
476
        if (m_link_map.only_unary_links()) {
56,982✔
477
            int64_t val = 0;
56,373✔
478
            if (sz == 1) {
56,373✔
479
                const Obj obj = m_link_map.get_target_table()->get_object(links[0]);
56,223✔
480
                val = obj._get<int64_t>(m_column_key.get_index());
56,223✔
481
            }
56,223✔
482
            destination.init(false, 1);
56,373✔
483
            destination.set(0, val);
56,373✔
484
        }
56,373✔
485
        else {
609✔
486
            destination.init(true, sz);
609✔
487
            for (size_t t = 0; t < sz; t++) {
3,471✔
488
                const Obj obj = m_link_map.get_target_table()->get_object(links[t]);
2,862✔
489
                int64_t val = obj._get<int64_t>(m_column_key.get_index());
2,862✔
490
                destination.set(t, val);
2,862✔
491
            }
2,862✔
492
        }
609✔
493
    }
56,982✔
494
    else {
1,958,688✔
495
        size_t rows = std::min(m_leaf->size() - index, nb_elements);
1,958,688✔
496

979,380✔
497
        destination.init(false, rows);
1,958,688✔
498

979,380✔
499
        for (size_t t = 0; t < rows; t++) {
3,917,025✔
500
            destination.set(t, m_leaf->get(index + t));
1,958,337✔
501
        }
1,958,337✔
502
    }
1,958,688✔
503
}
2,015,670✔
504

505
void LinkCount::evaluate(size_t index, ValueBase& destination)
506
{
22,836✔
507
    if (m_column_key) {
22,836✔
508
        REALM_ASSERT(m_link_map.has_links());
198✔
509
        std::vector<ObjKey> links = m_link_map.get_links(index);
198✔
510
        auto sz = links.size();
198✔
511

99✔
512
        if (sz == 0) {
198✔
513
            destination.init(true, 0);
36✔
514
        }
36✔
515
        else {
162✔
516
            destination.init(true, sz);
162✔
517
            Allocator& alloc = m_link_map.get_target_table()->get_alloc();
162✔
518
            for (size_t i = 0; i < sz; i++) {
330✔
519
                const Obj obj = m_link_map.get_target_table()->get_object(links[i]);
168✔
520
                auto val = obj._get<int64_t>(m_column_key.get_index());
168✔
521
                size_t s;
168✔
522
                if (m_column_key.get_type() == col_type_Link && !m_column_key.is_collection()) {
168✔
523
                    // It is a single link column
9✔
524
                    s = (val == 0) ? 0 : 1;
18✔
525
                }
18✔
526
                else if (val & 1) {
150✔
527
                    // It is a backlink column with just one value
27✔
528
                    s = 1;
54✔
529
                }
54✔
530
                else {
96✔
531
                    // This is some kind of collection or backlink column
48✔
532
                    s = _impl::get_collection_size_from_ref(to_ref(val), alloc);
96✔
533
                }
96✔
534
                destination.set(i, int64_t(s));
168✔
535
            }
168✔
536
        }
162✔
537
    }
198✔
538
    else {
22,638✔
539
        destination = Value<int64_t>(m_link_map.count_links(index));
22,638✔
540
    }
22,638✔
541
}
22,836✔
542

543
std::string LinkCount::description(util::serializer::SerialisationState& state) const
544
{
228✔
545
    return state.describe_columns(m_link_map, m_column_key) + util::serializer::value_separator + "@count";
228✔
546
}
228✔
547

548
Query Subexpr2<StringData>::equal(StringData sd, bool case_sensitive)
549
{
798✔
550
    return string_compare<StringData, Equal, EqualIns>(*this, sd, case_sensitive);
798✔
551
}
798✔
552

553
Query Subexpr2<StringData>::equal(const Subexpr2<StringData>& col, bool case_sensitive)
554
{
276✔
555
    return string_compare<Equal, EqualIns>(*this, col, case_sensitive);
276✔
556
}
276✔
557

558
Query Subexpr2<StringData>::not_equal(StringData sd, bool case_sensitive)
559
{
762✔
560
    return string_compare<StringData, NotEqual, NotEqualIns>(*this, sd, case_sensitive);
762✔
561
}
762✔
562

563
Query Subexpr2<StringData>::not_equal(const Subexpr2<StringData>& col, bool case_sensitive)
564
{
276✔
565
    return string_compare<NotEqual, NotEqualIns>(*this, col, case_sensitive);
276✔
566
}
276✔
567

568
Query Subexpr2<StringData>::begins_with(StringData sd, bool case_sensitive)
569
{
1,464✔
570
    return string_compare<StringData, BeginsWith, BeginsWithIns>(*this, sd, case_sensitive);
1,464✔
571
}
1,464✔
572

573
Query Subexpr2<StringData>::begins_with(const Subexpr2<StringData>& col, bool case_sensitive)
574
{
576✔
575
    return string_compare<BeginsWith, BeginsWithIns>(*this, col, case_sensitive);
576✔
576
}
576✔
577

578
Query Subexpr2<StringData>::ends_with(StringData sd, bool case_sensitive)
579
{
1,434✔
580
    return string_compare<StringData, EndsWith, EndsWithIns>(*this, sd, case_sensitive);
1,434✔
581
}
1,434✔
582

583
Query Subexpr2<StringData>::ends_with(const Subexpr2<StringData>& col, bool case_sensitive)
584
{
564✔
585
    return string_compare<EndsWith, EndsWithIns>(*this, col, case_sensitive);
564✔
586
}
564✔
587

588
Query Subexpr2<StringData>::contains(StringData sd, bool case_sensitive)
589
{
1,470✔
590
    return string_compare<StringData, Contains, ContainsIns>(*this, sd, case_sensitive);
1,470✔
591
}
1,470✔
592

593
Query Subexpr2<StringData>::contains(const Subexpr2<StringData>& col, bool case_sensitive)
594
{
570✔
595
    return string_compare<Contains, ContainsIns>(*this, col, case_sensitive);
570✔
596
}
570✔
597

598
Query Subexpr2<StringData>::like(StringData sd, bool case_sensitive)
599
{
1,440✔
600
    return string_compare<StringData, Like, LikeIns>(*this, sd, case_sensitive);
1,440✔
601
}
1,440✔
602

603
Query Subexpr2<StringData>::like(const Subexpr2<StringData>& col, bool case_sensitive)
604
{
558✔
605
    return string_compare<Like, LikeIns>(*this, col, case_sensitive);
558✔
606
}
558✔
607

608
Query Columns<StringData>::fulltext(StringData text) const
609
{
12✔
610
    const LinkMap& link_map = get_link_map();
12✔
611
    return link_map.get_base_table()->where().fulltext(column_key(), text, link_map);
12✔
612
}
12✔
613

614

615
// BinaryData
616

617
Query Subexpr2<BinaryData>::equal(BinaryData sd, bool case_sensitive)
618
{
×
619
    return binary_compare<BinaryData, Equal, EqualIns>(*this, sd, case_sensitive);
×
620
}
×
621

622
Query Subexpr2<BinaryData>::equal(const Subexpr2<BinaryData>& col, bool case_sensitive)
623
{
×
624
    return binary_compare<Equal, EqualIns>(*this, col, case_sensitive);
×
625
}
×
626

627
Query Subexpr2<BinaryData>::not_equal(BinaryData sd, bool case_sensitive)
628
{
×
629
    return binary_compare<BinaryData, NotEqual, NotEqualIns>(*this, sd, case_sensitive);
×
630
}
×
631

632
Query Subexpr2<BinaryData>::not_equal(const Subexpr2<BinaryData>& col, bool case_sensitive)
633
{
×
634
    return binary_compare<NotEqual, NotEqualIns>(*this, col, case_sensitive);
×
635
}
×
636

637
Query Subexpr2<BinaryData>::begins_with(BinaryData sd, bool case_sensitive)
638
{
×
639
    return binary_compare<BinaryData, BeginsWith, BeginsWithIns>(*this, sd, case_sensitive);
×
640
}
×
641

642
Query Subexpr2<BinaryData>::begins_with(const Subexpr2<BinaryData>& col, bool case_sensitive)
643
{
×
644
    return binary_compare<BeginsWith, BeginsWithIns>(*this, col, case_sensitive);
×
645
}
×
646

647
Query Subexpr2<BinaryData>::ends_with(BinaryData sd, bool case_sensitive)
648
{
×
649
    return binary_compare<BinaryData, EndsWith, EndsWithIns>(*this, sd, case_sensitive);
×
650
}
×
651

652
Query Subexpr2<BinaryData>::ends_with(const Subexpr2<BinaryData>& col, bool case_sensitive)
653
{
×
654
    return binary_compare<EndsWith, EndsWithIns>(*this, col, case_sensitive);
×
655
}
×
656

657
Query Subexpr2<BinaryData>::contains(BinaryData sd, bool case_sensitive)
658
{
×
659
    return binary_compare<BinaryData, Contains, ContainsIns>(*this, sd, case_sensitive);
×
660
}
×
661

662
Query Subexpr2<BinaryData>::contains(const Subexpr2<BinaryData>& col, bool case_sensitive)
663
{
×
664
    return binary_compare<Contains, ContainsIns>(*this, col, case_sensitive);
×
665
}
×
666

667
Query Subexpr2<BinaryData>::like(BinaryData sd, bool case_sensitive)
668
{
×
669
    return binary_compare<BinaryData, Like, LikeIns>(*this, sd, case_sensitive);
×
670
}
×
671

672
Query Subexpr2<BinaryData>::like(const Subexpr2<BinaryData>& col, bool case_sensitive)
673
{
×
674
    return binary_compare<Like, LikeIns>(*this, col, case_sensitive);
×
675
}
×
676

677
// Mixed
678

679
Query Subexpr2<Mixed>::equal(Mixed sd, bool case_sensitive)
680
{
6✔
681
    return mixed_compare<Mixed, Equal, EqualIns>(*this, sd, case_sensitive);
6✔
682
}
6✔
683

684
Query Subexpr2<Mixed>::equal(const Subexpr2<Mixed>& col, bool case_sensitive)
685
{
×
686
    return mixed_compare<Equal, EqualIns>(*this, col, case_sensitive);
×
687
}
×
688

689
Query Subexpr2<Mixed>::not_equal(Mixed sd, bool case_sensitive)
690
{
×
691
    return mixed_compare<Mixed, NotEqual, NotEqualIns>(*this, sd, case_sensitive);
×
692
}
×
693

694
Query Subexpr2<Mixed>::not_equal(const Subexpr2<Mixed>& col, bool case_sensitive)
695
{
×
696
    return mixed_compare<NotEqual, NotEqualIns>(*this, col, case_sensitive);
×
697
}
×
698

699
Query Subexpr2<Mixed>::begins_with(Mixed sd, bool case_sensitive)
700
{
12✔
701
    return mixed_compare<Mixed, BeginsWith, BeginsWithIns>(*this, sd, case_sensitive);
12✔
702
}
12✔
703

704
Query Subexpr2<Mixed>::begins_with(const Subexpr2<Mixed>& col, bool case_sensitive)
705
{
×
706
    return mixed_compare<BeginsWith, BeginsWithIns>(*this, col, case_sensitive);
×
707
}
×
708

709
Query Subexpr2<Mixed>::ends_with(Mixed sd, bool case_sensitive)
710
{
12✔
711
    return mixed_compare<Mixed, EndsWith, EndsWithIns>(*this, sd, case_sensitive);
12✔
712
}
12✔
713

714
Query Subexpr2<Mixed>::ends_with(const Subexpr2<Mixed>& col, bool case_sensitive)
715
{
×
716
    return mixed_compare<EndsWith, EndsWithIns>(*this, col, case_sensitive);
×
717
}
×
718

719
Query Subexpr2<Mixed>::contains(Mixed sd, bool case_sensitive)
720
{
12✔
721
    return mixed_compare<Mixed, Contains, ContainsIns>(*this, sd, case_sensitive);
12✔
722
}
12✔
723

724
Query Subexpr2<Mixed>::contains(const Subexpr2<Mixed>& col, bool case_sensitive)
725
{
×
726
    return mixed_compare<Contains, ContainsIns>(*this, col, case_sensitive);
×
727
}
×
728

729
Query Subexpr2<Mixed>::like(Mixed sd, bool case_sensitive)
730
{
12✔
731
    return mixed_compare<Mixed, Like, LikeIns>(*this, sd, case_sensitive);
12✔
732
}
12✔
733

734
Query Subexpr2<Mixed>::like(const Subexpr2<Mixed>& col, bool case_sensitive)
735
{
×
736
    return mixed_compare<Like, LikeIns>(*this, col, case_sensitive);
×
737
}
×
738

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