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

realm / realm-core / github_pull_request_278228

04 Oct 2023 10:15PM UTC coverage: 91.582% (+0.007%) from 91.575%
github_pull_request_278228

Pull #7029

Evergreen

tgoyne
Use UNITTEST_LOG_LEVEL in objectstore tests

For historical reasons core and sync tests use the UNITTEST_LOG_LEVEL
environment variable to determine the test log level, while object store tests
used a build time setting. This brings them into alignment on using the env
variable, and applies it via setting the default log level on startup in a
single place.
Pull Request #7029: Use UNITTEST_LOG_LEVEL in objectstore tests

94218 of 173442 branches covered (0.0%)

46 of 54 new or added lines in 5 files covered. (85.19%)

51 existing lines in 12 files now uncovered.

230351 of 251523 relevant lines covered (91.58%)

6704577.96 hits per line

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

87.03
/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,621✔
27
    if (table == get_base_table())
846,621✔
28
        return;
38,220✔
29

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

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

16,074✔
45
        m_link_types.push_back(type);
32,151✔
46
        REALM_ASSERT(table->valid_column(link_column_key));
32,151✔
47
        table = table.unchecked_ptr()->get_opposite_table(link_column_key);
32,151✔
48
        m_tables.push_back(table);
32,151✔
49
    }
32,151✔
50
}
808,401✔
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,449✔
64
    std::string s;
7,449✔
65
    for (size_t i = 0; i < m_link_column_keys.size(); ++i) {
17,058✔
66
        if (i < m_tables.size() && m_tables[i]) {
9,609✔
67
            s += state.get_column_name(m_tables[i], m_link_column_keys[i]);
9,609✔
68
            if (i != m_link_column_keys.size() - 1) {
9,609✔
69
                s += util::serializer::value_separator;
2,160✔
70
            }
2,160✔
71
        }
9,609✔
72
    }
9,609✔
73
    return s;
7,449✔
74
}
7,449✔
75

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

16,380✔
84
    ColumnType type = m_link_types[column];
32,739✔
85
    ColKey column_key = m_link_column_keys[column];
32,739✔
86
    const Obj obj = m_tables[column]->get_object(key);
32,739✔
87
    if (column_key.is_collection()) {
32,739✔
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,327✔
96
        return map_links(column + 1, obj.get<ObjKey>(column_key), lm);
29,652✔
97
    }
29,652✔
98
    else if (type == col_type_BackLink) {
675✔
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 {
21✔
106
        REALM_TERMINATE("Invalid column type in LinkMap::map_links()");
21✔
107
    }
21✔
108
    return true;
17,913✔
109
}
32,739✔
110

111
void LinkMap::map_links(size_t column, size_t row, LinkMapFunction lm) const
112
{
355,236✔
113
    ColumnType type = m_link_types[column];
355,236✔
114
    ColKey column_key = m_link_column_keys[column];
355,236✔
115
    if (type == col_type_Link && !column_key.is_set()) {
355,236✔
116
        if (column_key.is_dictionary()) {
155,286✔
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,350✔
140
            REALM_ASSERT(!column_key.is_collection());
154,350✔
141
            map_links(column + 1, mpark::get<ArrayKey>(m_leaf).get(row), lm);
154,350✔
142
        }
154,350✔
143
    }
155,286✔
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,921✔
155
            links.init_from_ref(ref);
138,921✔
156
            size_t sz = links.size();
138,921✔
157
            for (size_t t = 0; t < sz; t++) {
411,525✔
158
                if (!map_links(column + 1, links.get(t), lm))
272,910✔
159
                    break;
306✔
160
            }
272,910✔
161
        }
138,921✔
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,236✔
176

177
std::vector<ObjKey> LinkMap::get_origin_ndxs(ObjKey key, size_t column) const
178
{
19,368✔
179
    if (column == m_link_types.size()) {
19,368✔
180
        return {key};
15,195✔
181
    }
15,195✔
182
    std::vector<ObjKey> keys = get_origin_ndxs(key, column + 1);
4,173✔
183
    std::vector<ObjKey> ret;
4,173✔
184
    auto origin_col = m_link_column_keys[column];
4,173✔
185
    auto origin = m_tables[column];
4,173✔
186
    auto link_type = m_link_types[column];
4,173✔
187
    if (link_type == col_type_BackLink) {
4,173✔
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,513✔
207
        auto target = m_tables[column + 1];
3,513✔
208
        for (auto k : keys) {
3,627✔
209
            const Obj o = target->get_object(k);
3,627✔
210
            auto cnt = o.get_backlink_count(*origin, origin_col);
3,627✔
211
            for (size_t i = 0; i < cnt; i++) {
7,944✔
212
                ret.push_back(o.get_backlink(*origin, origin_col, i));
4,317✔
213
            }
4,317✔
214
        }
3,627✔
215
    }
3,513✔
216
    return ret;
4,173✔
217
}
4,173✔
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,700✔
461
    if (m_link_map.has_links()) {
38,700✔
462
        m_link_map.set_cluster(cluster);
8,022✔
463
    }
8,022✔
464
    else {
30,678✔
465
        m_leaf.emplace(m_link_map.get_base_table()->get_alloc());
30,678✔
466
        cluster->init_leaf(m_column_key, &*m_leaf);
30,678✔
467
    }
30,678✔
468
}
38,700✔
469

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

28,500✔
476
        if (m_link_map.only_unary_links()) {
57,000✔
477
            int64_t val = 0;
56,388✔
478
            if (sz == 1) {
56,388✔
479
                const Obj obj = m_link_map.get_target_table()->get_object(links[0]);
56,238✔
480
                val = obj._get<int64_t>(m_column_key.get_index());
56,238✔
481
            }
56,238✔
482
            destination.init(false, 1);
56,388✔
483
            destination.set(0, val);
56,388✔
484
        }
56,388✔
485
        else {
612✔
486
            destination.init(true, sz);
612✔
487
            for (size_t t = 0; t < sz; t++) {
3,474✔
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
        }
612✔
493
    }
57,000✔
494
    else {
1,958,130✔
495
        size_t rows = std::min(m_leaf->size() - index, nb_elements);
1,958,130✔
496

979,236✔
497
        destination.init(false, rows);
1,958,130✔
498

979,236✔
499
        for (size_t t = 0; t < rows; t++) {
3,915,027✔
500
            destination.set(t, m_leaf->get(index + t));
1,956,897✔
501
        }
1,956,897✔
502
    }
1,958,130✔
503
}
2,015,130✔
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