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

realm / realm-core / nicola.cabiddu_1042

27 Sep 2023 06:04PM UTC coverage: 91.085% (-1.8%) from 92.915%
nicola.cabiddu_1042

Pull #6766

Evergreen

nicola-cab
Fix logic for dictionaries
Pull Request #6766: Client Reset for collections in mixed / nested collections

97276 of 178892 branches covered (0.0%)

1994 of 2029 new or added lines in 7 files covered. (98.28%)

4556 existing lines in 112 files now uncovered.

237059 of 260260 relevant lines covered (91.09%)

6321099.55 hits per line

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

94.42
/src/realm/sort_descriptor.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/sort_descriptor.hpp>
20
#include <realm/table.hpp>
21
#include <realm/db.hpp>
22
#include <realm/util/assert.hpp>
23
#include <realm/list.hpp>
24
#include <realm/dictionary.hpp>
25

26
using namespace realm;
27

28
ConstTableRef ExtendedColumnKey::get_target_table(const Table* table) const
29
{
750✔
30
    return (m_colkey.get_type() == col_type_Link) ? table->get_link_target(m_colkey) : ConstTableRef{};
744✔
31
}
750✔
32

33
std::string ExtendedColumnKey::get_description(const Table* table) const
34
{
708✔
35
    std::string description = table->get_column_name(m_colkey);
708✔
36
    if (has_index()) {
708✔
37
        description += util::format("[%1]", util::serializer::print_value(m_index));
24✔
38
    }
24✔
39
    return description;
708✔
40
}
708✔
41

42
std::string ExtendedColumnKey::get_description(ConstTableRef table, util::serializer::SerialisationState& state) const
43
{
11,016✔
44
    std::string description = state.get_column_name(table, m_colkey);
11,016✔
45
    // m_index has the type col_key if it is not set
5,508✔
46
    if (has_index()) {
11,016✔
47
        description += util::format("[%1]", util::serializer::print_value(m_index));
90✔
48
    }
90✔
49
    return description;
11,016✔
50
}
11,016✔
51

52
bool ExtendedColumnKey::is_collection() const
53
{
19,713✔
54
    return m_colkey.is_collection() && !has_index();
19,713✔
55
}
19,713✔
56

57
ObjKey ExtendedColumnKey::get_link_target(const Obj& obj) const
58
{
3,162✔
59
    if (!has_index()) {
3,162✔
60
        return obj.get<ObjKey>(m_colkey);
3,126✔
61
    }
3,126✔
62
    else if (m_colkey.is_dictionary()) {
36✔
63
        const auto dictionary = obj.get_dictionary(m_colkey);
36✔
64
        auto val = dictionary.try_get(m_index.get_key());
36✔
65
        if (val && val->is_type(type_TypedLink)) {
36✔
66
            return val->get<ObjKey>();
36✔
67
        }
36✔
UNCOV
68
    }
×
UNCOV
69
    return {};
×
UNCOV
70
}
×
71

72
Mixed ExtendedColumnKey::get_value(const Obj& obj) const
73
{
758,502✔
74
    if (!has_index()) {
758,502✔
75
        return obj.get_any(m_colkey);
758,304✔
76
    }
758,304✔
77
    else if (m_colkey.is_dictionary()) {
198✔
78
        const auto dictionary = obj.get_dictionary(m_colkey);
198✔
79
        auto val = dictionary.try_get(m_index.get_key());
198✔
80
        if (val) {
198✔
81
            return *val;
198✔
82
        }
198✔
UNCOV
83
    }
×
UNCOV
84
    return {};
×
UNCOV
85
}
×
86

87
LinkPathPart::LinkPathPart(ColKey col_key, ConstTableRef source)
88
    : column_key(col_key)
89
    , from(source->get_key())
UNCOV
90
{
×
UNCOV
91
}
×
92

93

94
ColumnsDescriptor::ColumnsDescriptor(std::vector<std::vector<ExtendedColumnKey>> column_keys)
95
    : m_column_keys(std::move(column_keys))
96
{
37,887✔
97
}
37,887✔
98

99
std::unique_ptr<BaseDescriptor> DistinctDescriptor::clone() const
100
{
1,590✔
101
    return std::unique_ptr<DistinctDescriptor>(new DistinctDescriptor(*this));
1,590✔
102
}
1,590✔
103

104
void ColumnsDescriptor::collect_dependencies(const Table* table, std::vector<TableKey>& table_keys) const
105
{
50,601✔
106
    for (auto& columns : m_column_keys) {
51,249✔
107
        auto sz = columns.size();
51,249✔
108
        // If size is 0 or 1 there is no link chain and hence no additional tables to check
25,830✔
109
        if (sz > 1) {
51,249✔
110
            const Table* t = table;
558✔
111
            for (size_t i = 0; i < sz - 1; i++) {
1,200✔
112
                const auto& col = columns[i];
654✔
113
                ConstTableRef target_table = col.get_target_table(t);
654✔
114
                if (!target_table)
654✔
115
                    return;
12✔
116
                table_keys.push_back(target_table->get_key());
642✔
117
                t = target_table.unchecked_ptr();
642✔
118
            }
642✔
119
        }
558✔
120
    }
51,249✔
121
}
50,601✔
122

123
std::string DistinctDescriptor::get_description(ConstTableRef attached_table) const
124
{
210✔
125
    std::string description = "DISTINCT(";
210✔
126
    for (size_t i = 0; i < m_column_keys.size(); ++i) {
444✔
127
        const size_t chain_size = m_column_keys[i].size();
234✔
128
        ConstTableRef cur_link_table = attached_table;
234✔
129
        for (size_t j = 0; j < chain_size; ++j) {
504✔
130
            const auto& col_key = m_column_keys[i][j];
270✔
131
            description += col_key.get_description(cur_link_table.unchecked_ptr());
270✔
132
            if (j < chain_size - 1) {
270✔
133
                description += ".";
36✔
134
                cur_link_table = col_key.get_target_table(cur_link_table.unchecked_ptr());
36✔
135
            }
36✔
136
        }
270✔
137
        if (i < m_column_keys.size() - 1) {
234✔
138
            description += ", ";
24✔
139
        }
24✔
140
    }
234✔
141
    description += ")";
210✔
142
    return description;
210✔
143
}
210✔
144

145
std::string SortDescriptor::get_description(ConstTableRef attached_table) const
146
{
342✔
147
    std::string description = "SORT(";
342✔
148
    for (size_t i = 0; i < m_column_keys.size(); ++i) {
720✔
149
        const size_t chain_size = m_column_keys[i].size();
378✔
150
        ConstTableRef cur_link_table = attached_table;
378✔
151
        for (size_t j = 0; j < chain_size; ++j) {
816✔
152
            const auto& col_key = m_column_keys[i][j];
438✔
153
            description += col_key.get_description(cur_link_table.unchecked_ptr());
438✔
154
            if (j < chain_size - 1) {
438✔
155
                description += ".";
60✔
156
                cur_link_table = col_key.get_target_table(cur_link_table.unchecked_ptr());
60✔
157
            }
60✔
158
        }
438✔
159
        description += " ";
378✔
160
        if (i < m_ascending.size()) {
378✔
161
            if (m_ascending[i]) {
378✔
162
                description += "ASC";
288✔
163
            }
288✔
164
            else {
90✔
165
                description += "DESC";
90✔
166
            }
90✔
167
        }
378✔
168
        if (i < m_column_keys.size() - 1) {
378✔
169
            description += ", ";
36✔
170
        }
36✔
171
    }
378✔
172
    description += ")";
342✔
173
    return description;
342✔
174
}
342✔
175

176
SortDescriptor::SortDescriptor(std::vector<std::vector<ExtendedColumnKey>> column_keys, std::vector<bool> ascending)
177
    : ColumnsDescriptor(std::move(column_keys))
178
    , m_ascending(std::move(ascending))
179
{
35,757✔
180
    REALM_ASSERT_EX(m_ascending.empty() || m_ascending.size() == m_column_keys.size(), m_ascending.size(),
35,757✔
181
                    m_column_keys.size());
35,757✔
182
    if (m_ascending.empty())
35,757✔
183
        m_ascending.resize(m_column_keys.size(), true);
114✔
184
}
35,757✔
185

186
std::unique_ptr<BaseDescriptor> SortDescriptor::clone() const
187
{
78,063✔
188
    return std::unique_ptr<ColumnsDescriptor>(new SortDescriptor(*this));
78,063✔
189
}
78,063✔
190

191
void SortDescriptor::merge(SortDescriptor&& other, MergeMode mode)
192
{
102✔
193
    if (mode == MergeMode::replace) {
102✔
194
        m_column_keys = std::move(other.m_column_keys);
6✔
195
        m_ascending = std::move(other.m_ascending);
6✔
196
        return;
6✔
197
    }
6✔
198

48✔
199
    m_column_keys.insert(mode == MergeMode::prepend ? m_column_keys.begin() : m_column_keys.end(),
96✔
200
                         other.m_column_keys.begin(), other.m_column_keys.end());
96✔
201
    // Do not use a move iterator on a vector of bools!
48✔
202
    // It will form a reference to a temporary and return incorrect results.
48✔
203
    m_ascending.insert(mode == MergeMode::prepend ? m_ascending.begin() : m_ascending.end(),
93✔
204
                       other.m_ascending.begin(), other.m_ascending.end());
96✔
205
}
96✔
206

207
BaseDescriptor::Sorter::Sorter(std::vector<std::vector<ExtendedColumnKey>> const& column_lists,
208
                               std::vector<bool> const& ascending, Table const& root_table, const IndexPairs& indexes)
209
{
19,011✔
210
    REALM_ASSERT(!column_lists.empty());
19,011✔
211
    REALM_ASSERT_EX(column_lists.size() == ascending.size(), column_lists.size(), ascending.size());
19,011✔
212
    size_t translated_size = std::max_element(indexes.begin(), indexes.end())->index_in_view + 1;
19,011✔
213

9,510✔
214
    m_columns.reserve(column_lists.size());
19,011✔
215
    for (size_t i = 0; i < column_lists.size(); ++i) {
38,700✔
216
        auto& columns = column_lists[i];
19,713✔
217
        auto sz = columns.size();
19,713✔
218
        REALM_ASSERT_EX(!columns.empty(), i);
19,713✔
219

9,861✔
220
        if (columns.empty()) {
19,713✔
UNCOV
221
            throw InvalidArgument(ErrorCodes::InvalidSortDescriptor, "Missing property");
×
UNCOV
222
        }
×
223
        if (columns.empty() || columns.back().is_collection()) {
19,713✔
224
            throw InvalidArgument(ErrorCodes::InvalidSortDescriptor, "Cannot sort on a collection property");
6✔
225
        }
6✔
226

9,858✔
227
        if (sz == 1) { // no link chain
19,707✔
228
            m_columns.emplace_back(&root_table, columns[0], ascending[i]);
19,143✔
229
            continue;
19,143✔
230
        }
19,143✔
231

282✔
232
        std::vector<const Table*> tables = {&root_table};
564✔
233
        tables.resize(sz);
564✔
234
        for (size_t j = 0; j + 1 < sz; ++j) {
1,212✔
235
            ColKey col = columns[j];
666✔
236
            if (!tables[j]->valid_column(col)) {
666✔
237
                throw InvalidArgument(ErrorCodes::InvalidSortDescriptor, "Invalid property");
6✔
238
            }
6✔
239
            if (col.get_type() != col_type_Link) {
660✔
240
                // Only last column in link chain is allowed to be non-link
6✔
241
                throw InvalidArgument(ErrorCodes::InvalidSortDescriptor, "All but last property must be a link");
12✔
242
            }
12✔
243
            tables[j + 1] = tables[j]->get_link_target(col).unchecked_ptr();
648✔
244
        }
648✔
245

282✔
246
        m_columns.emplace_back(tables.back(), columns.back(), ascending[i]);
555✔
247

273✔
248
        auto& translated_keys = m_columns.back().translated_keys;
546✔
249
        translated_keys.resize(translated_size);
546✔
250

273✔
251
        for (const auto& index : indexes) {
2,646✔
252
            size_t index_in_view = index.index_in_view;
2,646✔
253
            ObjKey translated_key = index.key_for_object;
2,646✔
254
            for (size_t j = 0; j + 1 < sz; ++j) {
5,574✔
255
                const Obj obj = tables[j]->get_object(translated_key);
3,162✔
256
                // type was checked when creating the ColumnsDescriptor
1,581✔
257
                translated_key = columns[j].get_link_target(obj);
3,162✔
258
                if (!translated_key || translated_key.is_unresolved()) {
3,162✔
259
                    translated_key = null_key; // normalize unresolve to null
234✔
260
                    break;
234✔
261
                }
234✔
262
            }
3,162✔
263
            translated_keys[index_in_view] = translated_key;
2,646✔
264
        }
2,646✔
265
    }
546✔
266
    m_cache.resize(column_lists.size() - 1);
18,999✔
267
}
18,987✔
268

269
BaseDescriptor::Sorter DistinctDescriptor::sorter(Table const& table, const IndexPairs& indexes) const
270
{
1,188✔
271
    REALM_ASSERT(!m_column_keys.empty());
1,188✔
272
    std::vector<bool> ascending(m_column_keys.size(), true);
1,188✔
273
    return Sorter(m_column_keys, ascending, table, indexes);
1,188✔
274
}
1,188✔
275

276
void DistinctDescriptor::execute(IndexPairs& v, const Sorter& predicate, const BaseDescriptor* next) const
277
{
1,188✔
278
    using IP = ColumnsDescriptor::IndexPair;
1,188✔
279
    // Remove all rows which have a null link along the way to the distinct columns
594✔
280
    if (predicate.has_links()) {
1,188✔
281
        auto nulls = std::remove_if(v.begin(), v.end(), [&](const IP& index) {
1,026✔
282
            return predicate.any_is_null(index);
1,026✔
283
        });
1,026✔
284
        v.erase(nulls, v.end());
198✔
285
    }
198✔
286

594✔
287
    // Sort by the columns to distinct on
594✔
288
    std::sort(v.begin(), v.end(), std::ref(predicate));
1,188✔
289

594✔
290
    // Move duplicates to the back - "not less than" is "equal" since they're sorted
594✔
291
    auto duplicates = std::unique(v.begin(), v.end(), [&](const IP& a, const IP& b) {
5,382✔
292
        return !predicate(a, b, false);
5,382✔
293
    });
5,382✔
294
    // Erase the duplicates
594✔
295
    v.erase(duplicates, v.end());
1,188✔
296
    bool will_be_sorted_next = next && next->get_type() == DescriptorType::Sort;
1,188✔
297
    if (!will_be_sorted_next) {
1,188✔
298
        // Restore the original order, this is either the original
486✔
299
        // tableview order or the order of the previous sort
486✔
300
        std::sort(v.begin(), v.end(), [](const IP& a, const IP& b) {
3,609✔
301
            return a.index_in_view < b.index_in_view;
3,609✔
302
        });
3,609✔
303
    }
972✔
304
}
1,188✔
305

306
BaseDescriptor::Sorter SortDescriptor::sorter(Table const& table, const IndexPairs& indexes) const
307
{
17,823✔
308
    REALM_ASSERT(!m_column_keys.empty());
17,823✔
309
    return Sorter(m_column_keys, m_ascending, table, indexes);
17,823✔
310
}
17,823✔
311

312
void SortDescriptor::execute(IndexPairs& v, const Sorter& predicate, const BaseDescriptor* next) const
313
{
17,799✔
314
    size_t limit = size_t(-1);
17,799✔
315
    if (next && next->get_type() == DescriptorType::Limit) {
17,799✔
316
        limit = static_cast<const LimitDescriptor*>(next)->get_limit();
1,674✔
317
    }
1,674✔
318
    // Measurements shows that if limit is smaller than size / 16, then
8,904✔
319
    // it is quicker to make a sorted insert into a smaller vector
8,904✔
320
    if (limit < (v.size() >> 4)) {
17,799✔
321
        IndexPairs buffer;
6✔
322
        buffer.reserve(limit + 1);
6✔
323
        for (auto& elem : v) {
60,000✔
324
            auto it = std::lower_bound(buffer.begin(), buffer.end(), elem, std::ref(predicate));
60,000✔
325
            buffer.insert(it, elem);
60,000✔
326
            if (buffer.size() > limit) {
60,000✔
327
                buffer.pop_back();
59,400✔
328
            }
59,400✔
329
        }
60,000✔
330
        v.m_removed_by_limit += v.size() - limit;
6✔
331
        v.erase(v.begin() + limit, v.end());
6✔
332
        std::move(buffer.begin(), buffer.end(), v.begin());
6✔
333
    }
6✔
334
    else {
17,793✔
335
        std::sort(v.begin(), v.end(), std::ref(predicate));
17,793✔
336
    }
17,793✔
337

8,904✔
338
    // not doing this on the last step is an optimisation
8,904✔
339
    if (next) {
17,799✔
340
        const size_t v_size = v.size();
2,016✔
341
        // Distinct must choose the winning unique elements by sorted
1,014✔
342
        // order not by the previous tableview order, the lowest
1,014✔
343
        // "index_in_view" wins.
1,014✔
344
        for (size_t i = 0; i < v_size; ++i) {
6,654✔
345
            v[i].index_in_view = i;
4,638✔
346
        }
4,638✔
347
    }
2,016✔
348
}
17,799✔
349

350
std::string LimitDescriptor::get_description(ConstTableRef) const
351
{
246✔
352
    return "LIMIT(" + util::serializer::print_value(m_limit) + ")";
246✔
353
}
246✔
354

355
std::unique_ptr<BaseDescriptor> LimitDescriptor::clone() const
356
{
5,748✔
357
    return std::unique_ptr<BaseDescriptor>(new LimitDescriptor(*this));
5,748✔
358
}
5,748✔
359

360
void LimitDescriptor::execute(IndexPairs& v, const Sorter&, const BaseDescriptor*) const
361
{
2,022✔
362
    if (v.size() > m_limit) {
2,022✔
363
        v.m_removed_by_limit += v.size() - m_limit;
432✔
364
        v.erase(v.begin() + m_limit, v.end());
432✔
365
    }
432✔
366
}
2,022✔
367

368

369
// This function must conform to 'is less' predicate - that is:
370
// return true if i is strictly smaller than j
371
bool BaseDescriptor::Sorter::operator()(IndexPair i, IndexPair j, bool total_ordering) const
372
{
2,359,233✔
373
    // Sorting can be specified by multiple columns, so that if two entries in the first column are
1,201,932✔
374
    // identical, then the rows are ordered according to the second column, and so forth. For the
1,201,932✔
375
    // first column, all the payload of the View is cached in IndexPair::cached_value.
1,201,932✔
376
    for (size_t t = 0; t < m_columns.size(); t++) {
3,652,446✔
377
        ObjKey key_i = i.key_for_object;
3,303,816✔
378
        ObjKey key_j = j.key_for_object;
3,303,816✔
379

1,676,328✔
380
        if (!m_columns[t].translated_keys.empty()) {
3,303,816✔
381
            key_i = m_columns[t].translated_keys[i.index_in_view];
4,476✔
382
            key_j = m_columns[t].translated_keys[j.index_in_view];
4,476✔
383

2,523✔
384
            bool null_i = !key_i;
4,476✔
385
            bool null_j = !key_j;
4,476✔
386

2,523✔
387
            if (null_i && null_j) {
4,476✔
388
                continue;
126✔
389
            }
126✔
390
            if (null_i || null_j) {
4,350✔
391
                // Sort null links at the end if m_ascending[t], else at beginning.
102✔
392
                return m_columns[t].ascending != null_i;
201✔
393
            }
201✔
394
        }
3,303,489✔
395

1,676,148✔
396
        int c;
3,303,489✔
397

1,676,148✔
398
        if (t == 0) {
3,303,489✔
399
            c = i.cached_value.compare(j.cached_value);
2,358,945✔
400
        }
2,358,945✔
401
        else {
944,544✔
402
            if (m_cache[t - 1].empty()) {
944,544✔
403
                m_cache[t - 1].resize(256);
588✔
404
            }
588✔
405
            ObjCache& cache_i = m_cache[t - 1][key_i.value & 0xFF];
944,544✔
406
            ObjCache& cache_j = m_cache[t - 1][key_j.value & 0xFF];
944,544✔
407

474,372✔
408
            if (cache_i.key != key_i) {
944,544✔
409
                const auto& obj = m_columns[t].table->get_object(key_i);
371,826✔
410
                const auto& col_key = m_columns[t].col_key;
371,826✔
411

147,093✔
412
                cache_i.value = col_key.get_value(obj);
371,826✔
413
                cache_i.key = key_i;
371,826✔
414
            }
371,826✔
415
            Mixed val_i = cache_i.value;
944,544✔
416

474,372✔
417
            if (cache_j.key != key_j) {
944,544✔
418
                const auto& obj = m_columns[t].table->get_object(key_j);
138,801✔
419
                const auto& col_key = m_columns[t].col_key;
138,801✔
420

128,715✔
421
                cache_j.value = col_key.get_value(obj);
138,801✔
422
                cache_j.key = key_j;
138,801✔
423
            }
138,801✔
424

474,372✔
425
            c = val_i.compare(cache_j.value);
944,544✔
426
        }
944,544✔
427
        // if c is negative i comes before j
1,676,148✔
428
        if (c) {
3,303,489✔
429
            return m_columns[t].ascending ? c < 0 : c > 0;
1,869,903✔
430
        }
2,010,402✔
431
    }
3,303,489✔
432
    // make sort stable by using original index as final comparison
1,201,932✔
433
    return total_ordering ? i.index_in_view < j.index_in_view : 0;
1,378,026✔
434
}
2,359,233✔
435

436
void BaseDescriptor::Sorter::cache_first_column(IndexPairs& v)
437
{
21,009✔
438
    if (m_columns.empty())
21,009✔
439
        return;
2,022✔
440

9,498✔
441
    auto& col = m_columns[0];
18,987✔
442
    const auto& ck = col.col_key;
18,987✔
443
    for (size_t i = 0; i < v.size(); i++) {
267,042✔
444
        IndexPair& index = v[i];
248,055✔
445
        ObjKey key = index.key_for_object;
248,055✔
446

124,026✔
447
        if (!col.translated_keys.empty()) {
248,055✔
448
            key = col.translated_keys[v[i].index_in_view];
2,340✔
449
            if (!key) {
2,340✔
450
                index.cached_value = Mixed();
180✔
451
                continue;
180✔
452
            }
180✔
453
        }
247,875✔
454

123,936✔
455
        const auto obj = col.table->get_object(key);
247,875✔
456
        index.cached_value = ck.get_value(obj);
247,875✔
457
    }
247,875✔
458
}
18,987✔
459

460
DescriptorOrdering::DescriptorOrdering(const DescriptorOrdering& other)
461
    : AtomicRefCountBase()
462
{
45,276✔
463
    for (const auto& d : other.m_descriptors) {
28,362✔
464
        m_descriptors.emplace_back(d->clone());
11,460✔
465
    }
11,460✔
466
}
45,276✔
467

468
DescriptorOrdering& DescriptorOrdering::operator=(const DescriptorOrdering& rhs)
469
{
98,151✔
470
    if (&rhs != this) {
98,151✔
471
        m_descriptors.clear();
98,151✔
472
        for (const auto& d : rhs.m_descriptors) {
86,397✔
473
            m_descriptors.emplace_back(d->clone());
73,893✔
474
        }
73,893✔
475
    }
98,151✔
476
    return *this;
98,151✔
477
}
98,151✔
478

479
void DescriptorOrdering::append_sort(SortDescriptor sort, SortDescriptor::MergeMode mode)
480
{
77,178✔
481
    if (!sort.is_valid()) {
77,178✔
482
        return;
41,421✔
483
    }
41,421✔
484
    if (!m_descriptors.empty()) {
35,757✔
485
        if (SortDescriptor* previous_sort = dynamic_cast<SortDescriptor*>(m_descriptors.back().get())) {
858✔
486
            previous_sort->merge(std::move(sort), mode);
102✔
487
            return;
102✔
488
        }
102✔
489
    }
35,655✔
490
    m_descriptors.emplace_back(new SortDescriptor(std::move(sort)));
35,655✔
491
}
35,655✔
492

493
void DescriptorOrdering::append_distinct(DistinctDescriptor distinct)
494
{
2,196✔
495
    if (distinct.is_valid()) {
2,196✔
496
        m_descriptors.emplace_back(new DistinctDescriptor(std::move(distinct)));
2,130✔
497
    }
2,130✔
498
}
2,196✔
499

500
void DescriptorOrdering::append_limit(LimitDescriptor limit)
501
{
4,434✔
502
    if (limit.is_valid()) {
4,434✔
503
        m_descriptors.emplace_back(new LimitDescriptor(std::move(limit)));
4,428✔
504
    }
4,428✔
505
}
4,434✔
506

507
void DescriptorOrdering::append(const DescriptorOrdering& other)
508
{
48✔
509
    for (const auto& d : other.m_descriptors) {
48✔
510
        m_descriptors.emplace_back(d->clone());
48✔
511
    }
48✔
512
}
48✔
513

514
void DescriptorOrdering::append(DescriptorOrdering&& other)
515
{
522✔
516
    std::move(other.m_descriptors.begin(), other.m_descriptors.end(), std::back_inserter(m_descriptors));
522✔
517
    other.m_descriptors.clear();
522✔
518
}
522✔
519

520
DescriptorType DescriptorOrdering::get_type(size_t index) const
521
{
22,935✔
522
    REALM_ASSERT(index < m_descriptors.size());
22,935✔
523
    return m_descriptors[index]->get_type();
22,935✔
524
}
22,935✔
525

526
const BaseDescriptor* DescriptorOrdering::operator[](size_t ndx) const
527
{
31,617✔
528
    if (ndx >= m_descriptors.size()) {
31,617✔
UNCOV
529
        throw OutOfBounds("DescriptorOrdering[]", ndx, m_descriptors.size());
×
UNCOV
530
    }
×
531
    return m_descriptors[ndx].get();
31,617✔
532
}
31,617✔
533

534
bool DescriptorOrdering::will_apply_sort() const
535
{
10,974✔
536
    return std::any_of(m_descriptors.begin(), m_descriptors.end(), [](const std::unique_ptr<BaseDescriptor>& desc) {
9,093✔
537
        REALM_ASSERT(desc->is_valid());
7,212✔
538
        return desc->get_type() == DescriptorType::Sort;
7,212✔
539
    });
7,212✔
540
}
10,974✔
541

542
bool DescriptorOrdering::will_apply_distinct() const
543
{
166,179✔
544
    return std::any_of(m_descriptors.begin(), m_descriptors.end(), [](const std::unique_ptr<BaseDescriptor>& desc) {
96,489✔
545
        REALM_ASSERT(desc->is_valid());
28,704✔
546
        return desc->get_type() == DescriptorType::Distinct;
28,704✔
547
    });
28,704✔
548
}
166,179✔
549

550
bool DescriptorOrdering::will_apply_limit() const
551
{
126✔
552
    return std::any_of(m_descriptors.begin(), m_descriptors.end(), [](const std::unique_ptr<BaseDescriptor>& desc) {
186✔
553
        REALM_ASSERT(desc->is_valid());
186✔
554
        return desc->get_type() == DescriptorType::Limit;
186✔
555
    });
186✔
556
}
126✔
557

558
realm::util::Optional<size_t> DescriptorOrdering::get_min_limit() const
559
{
80,688✔
560
    realm::util::Optional<size_t> min_limit;
80,688✔
561
    for (auto it = m_descriptors.begin(); it != m_descriptors.end(); it++) {
95,004✔
562
        if ((*it)->get_type() == DescriptorType::Limit) {
14,316✔
563
            const LimitDescriptor* limit = static_cast<const LimitDescriptor*>(it->get());
240✔
564
            REALM_ASSERT(limit);
240✔
565
            min_limit = bool(min_limit) ? std::min(*min_limit, limit->get_limit()) : limit->get_limit();
234✔
566
        }
240✔
567
    }
14,316✔
568
    return min_limit;
80,688✔
569
}
80,688✔
570

571
util::Optional<size_t> DescriptorOrdering::remove_all_limits()
572
{
×
573
    size_t min_limit = size_t(-1);
×
574
    for (auto it = m_descriptors.begin(); it != m_descriptors.end();) {
×
575
        if ((*it)->get_type() == DescriptorType::Limit) {
×
576
            const LimitDescriptor* limit = static_cast<const LimitDescriptor*>(it->get());
×
577
            if (limit->get_limit() < min_limit) {
×
UNCOV
578
                min_limit = limit->get_limit();
×
UNCOV
579
            }
×
UNCOV
580
            it = m_descriptors.erase(it);
×
UNCOV
581
        }
×
UNCOV
582
        else {
×
UNCOV
583
            ++it;
×
UNCOV
584
        }
×
UNCOV
585
    }
×
UNCOV
586
    return min_limit == size_t(-1) ? util::none : util::some<size_t>(min_limit);
×
UNCOV
587
}
×
588

589
bool DescriptorOrdering::will_limit_to_zero() const
590
{
84✔
591
    return std::any_of(m_descriptors.begin(), m_descriptors.end(), [](const std::unique_ptr<BaseDescriptor>& desc) {
192✔
592
        REALM_ASSERT(desc.get()->is_valid());
192✔
593
        return (desc->get_type() == DescriptorType::Limit &&
192✔
594
                static_cast<LimitDescriptor*>(desc.get())->get_limit() == 0);
114✔
595
    });
192✔
596
}
84✔
597

598
std::string DescriptorOrdering::get_description(ConstTableRef target_table) const
599
{
528✔
600
    std::string description = "";
528✔
601
    for (auto it = m_descriptors.begin(); it != m_descriptors.end(); ++it) {
1,326✔
602
        REALM_ASSERT_DEBUG(bool(*it));
798✔
603
        description += (*it)->get_description(target_table);
798✔
604
        if (it != m_descriptors.end() - 1) {
798✔
605
            description += " ";
342✔
606
        }
342✔
607
    }
798✔
608
    return description;
528✔
609
}
528✔
610

611
void DescriptorOrdering::collect_dependencies(const Table* table)
612
{
65,271✔
613
    m_dependencies.clear();
65,271✔
614
    for (auto& descr : m_descriptors) {
60,747✔
615
        descr->collect_dependencies(table, m_dependencies);
56,229✔
616
    }
56,229✔
617
}
65,271✔
618

619
void DescriptorOrdering::get_versions(const Group* group, TableVersions& versions) const
620
{
1,958,481✔
621
    for (auto table_key : m_dependencies) {
930,012✔
622
        REALM_ASSERT_DEBUG(group);
576✔
623
        versions.emplace_back(table_key, group->get_table(table_key)->get_content_version());
576✔
624
    }
576✔
625
}
1,958,481✔
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