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

realm / realm-core / 1704

25 Sep 2023 01:32PM UTC coverage: 91.198% (-0.02%) from 91.215%
1704

push

Evergreen

web-flow
Merge pull request #7001 from realm/release/13.21.0

Release 13.21

95872 of 175714 branches covered (0.0%)

232403 of 254833 relevant lines covered (91.2%)

7022166.79 hits per line

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

94.33
/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 (!m_index.is_null()) {
708✔
37
        description += util::format("[%1]", util::serializer::print_value(m_index));
24✔
38
    }
24✔
39
    return description;
708✔
40
}
708✔
41

42
bool ExtendedColumnKey::is_collection() const
43
{
19,743✔
44
    return m_colkey.is_collection() && m_index.is_null();
19,743✔
45
}
19,743✔
46

47
ObjKey ExtendedColumnKey::get_link_target(const Obj& obj) const
48
{
3,162✔
49
    if (m_index.is_null()) {
3,162✔
50
        return obj.get<ObjKey>(m_colkey);
3,126✔
51
    }
3,126✔
52
    else if (m_colkey.is_dictionary()) {
36✔
53
        const auto dictionary = obj.get_dictionary(m_colkey);
36✔
54
        auto val = dictionary.try_get(m_index);
36✔
55
        if (val && val->is_type(type_TypedLink)) {
36✔
56
            return val->get<ObjKey>();
36✔
57
        }
36✔
58
    }
×
59
    return {};
×
60
}
×
61

62
Mixed ExtendedColumnKey::get_value(const Obj& obj) const
63
{
770,073✔
64
    if (m_index.is_null()) {
770,073✔
65
        return obj.get_any(m_colkey);
769,875✔
66
    }
769,875✔
67
    else if (m_colkey.is_dictionary()) {
198✔
68
        const auto dictionary = obj.get_dictionary(m_colkey);
198✔
69
        auto val = dictionary.try_get(m_index);
198✔
70
        if (val) {
198✔
71
            return *val;
198✔
72
        }
198✔
73
    }
×
74
    return {};
×
75
}
×
76

77
LinkPathPart::LinkPathPart(ColKey col_key, ConstTableRef source)
78
    : column_key(col_key)
79
    , from(source->get_key())
80
{
×
81
}
×
82

83

84
ColumnsDescriptor::ColumnsDescriptor(std::vector<std::vector<ExtendedColumnKey>> column_keys)
85
    : m_column_keys(std::move(column_keys))
86
{
37,980✔
87
}
37,980✔
88

89
std::unique_ptr<BaseDescriptor> DistinctDescriptor::clone() const
90
{
1,590✔
91
    return std::unique_ptr<DistinctDescriptor>(new DistinctDescriptor(*this));
1,590✔
92
}
1,590✔
93

94
void ColumnsDescriptor::collect_dependencies(const Table* table, std::vector<TableKey>& table_keys) const
95
{
50,694✔
96
    for (auto& columns : m_column_keys) {
51,342✔
97
        auto sz = columns.size();
51,342✔
98
        // If size is 0 or 1 there is no link chain and hence no additional tables to check
25,893✔
99
        if (sz > 1) {
51,342✔
100
            const Table* t = table;
558✔
101
            for (size_t i = 0; i < sz - 1; i++) {
1,200✔
102
                const auto& col = columns[i];
654✔
103
                ConstTableRef target_table = col.get_target_table(t);
654✔
104
                if (!target_table)
654✔
105
                    return;
12✔
106
                table_keys.push_back(target_table->get_key());
642✔
107
                t = target_table.unchecked_ptr();
642✔
108
            }
642✔
109
        }
558✔
110
    }
51,342✔
111
}
50,694✔
112

113
std::string DistinctDescriptor::get_description(ConstTableRef attached_table) const
114
{
210✔
115
    std::string description = "DISTINCT(";
210✔
116
    for (size_t i = 0; i < m_column_keys.size(); ++i) {
444✔
117
        const size_t chain_size = m_column_keys[i].size();
234✔
118
        ConstTableRef cur_link_table = attached_table;
234✔
119
        for (size_t j = 0; j < chain_size; ++j) {
504✔
120
            const auto& col_key = m_column_keys[i][j];
270✔
121
            description += col_key.get_description(cur_link_table.unchecked_ptr());
270✔
122
            if (j < chain_size - 1) {
270✔
123
                description += ".";
36✔
124
                cur_link_table = col_key.get_target_table(cur_link_table.unchecked_ptr());
36✔
125
            }
36✔
126
        }
270✔
127
        if (i < m_column_keys.size() - 1) {
234✔
128
            description += ", ";
24✔
129
        }
24✔
130
    }
234✔
131
    description += ")";
210✔
132
    return description;
210✔
133
}
210✔
134

135
std::string SortDescriptor::get_description(ConstTableRef attached_table) const
136
{
342✔
137
    std::string description = "SORT(";
342✔
138
    for (size_t i = 0; i < m_column_keys.size(); ++i) {
720✔
139
        const size_t chain_size = m_column_keys[i].size();
378✔
140
        ConstTableRef cur_link_table = attached_table;
378✔
141
        for (size_t j = 0; j < chain_size; ++j) {
816✔
142
            const auto& col_key = m_column_keys[i][j];
438✔
143
            description += col_key.get_description(cur_link_table.unchecked_ptr());
438✔
144
            if (j < chain_size - 1) {
438✔
145
                description += ".";
60✔
146
                cur_link_table = col_key.get_target_table(cur_link_table.unchecked_ptr());
60✔
147
            }
60✔
148
        }
438✔
149
        description += " ";
378✔
150
        if (i < m_ascending.size()) {
378✔
151
            if (m_ascending[i]) {
378✔
152
                description += "ASC";
288✔
153
            }
288✔
154
            else {
90✔
155
                description += "DESC";
90✔
156
            }
90✔
157
        }
378✔
158
        if (i < m_column_keys.size() - 1) {
378✔
159
            description += ", ";
36✔
160
        }
36✔
161
    }
378✔
162
    description += ")";
342✔
163
    return description;
342✔
164
}
342✔
165

166
SortDescriptor::SortDescriptor(std::vector<std::vector<ExtendedColumnKey>> column_keys, std::vector<bool> ascending)
167
    : ColumnsDescriptor(std::move(column_keys))
168
    , m_ascending(std::move(ascending))
169
{
35,850✔
170
    REALM_ASSERT_EX(m_ascending.empty() || m_ascending.size() == m_column_keys.size(), m_ascending.size(),
35,850✔
171
                    m_column_keys.size());
35,850✔
172
    if (m_ascending.empty())
35,850✔
173
        m_ascending.resize(m_column_keys.size(), true);
114✔
174
}
35,850✔
175

176
std::unique_ptr<BaseDescriptor> SortDescriptor::clone() const
177
{
78,138✔
178
    return std::unique_ptr<ColumnsDescriptor>(new SortDescriptor(*this));
78,138✔
179
}
78,138✔
180

181
void SortDescriptor::merge(SortDescriptor&& other, MergeMode mode)
182
{
102✔
183
    if (mode == MergeMode::replace) {
102✔
184
        m_column_keys = std::move(other.m_column_keys);
6✔
185
        m_ascending = std::move(other.m_ascending);
6✔
186
        return;
6✔
187
    }
6✔
188

48✔
189
    m_column_keys.insert(mode == MergeMode::prepend ? m_column_keys.begin() : m_column_keys.end(),
96✔
190
                         other.m_column_keys.begin(), other.m_column_keys.end());
96✔
191
    // Do not use a move iterator on a vector of bools!
48✔
192
    // It will form a reference to a temporary and return incorrect results.
48✔
193
    m_ascending.insert(mode == MergeMode::prepend ? m_ascending.begin() : m_ascending.end(),
93✔
194
                       other.m_ascending.begin(), other.m_ascending.end());
96✔
195
}
96✔
196

197
BaseDescriptor::Sorter::Sorter(std::vector<std::vector<ExtendedColumnKey>> const& column_lists,
198
                               std::vector<bool> const& ascending, Table const& root_table, const IndexPairs& indexes)
199
{
19,041✔
200
    REALM_ASSERT(!column_lists.empty());
19,041✔
201
    REALM_ASSERT_EX(column_lists.size() == ascending.size(), column_lists.size(), ascending.size());
19,041✔
202
    size_t translated_size = std::max_element(indexes.begin(), indexes.end())->index_in_view + 1;
19,041✔
203

9,525✔
204
    m_columns.reserve(column_lists.size());
19,041✔
205
    for (size_t i = 0; i < column_lists.size(); ++i) {
38,760✔
206
        auto& columns = column_lists[i];
19,743✔
207
        auto sz = columns.size();
19,743✔
208
        REALM_ASSERT_EX(!columns.empty(), i);
19,743✔
209

9,876✔
210
        if (columns.empty()) {
19,743✔
211
            throw InvalidArgument(ErrorCodes::InvalidSortDescriptor, "Missing property");
×
212
        }
×
213
        if (columns.empty() || columns.back().is_collection()) {
19,743✔
214
            throw InvalidArgument(ErrorCodes::InvalidSortDescriptor, "Cannot sort on a collection property");
6✔
215
        }
6✔
216

9,873✔
217
        if (sz == 1) { // no link chain
19,737✔
218
            m_columns.emplace_back(&root_table, columns[0], ascending[i]);
19,173✔
219
            continue;
19,173✔
220
        }
19,173✔
221

282✔
222
        std::vector<const Table*> tables = {&root_table};
564✔
223
        tables.resize(sz);
564✔
224
        for (size_t j = 0; j + 1 < sz; ++j) {
1,212✔
225
            ColKey col = columns[j].get_col_key();
666✔
226
            if (!tables[j]->valid_column(col)) {
666✔
227
                throw InvalidArgument(ErrorCodes::InvalidSortDescriptor, "Invalid property");
6✔
228
            }
6✔
229
            if (col.get_type() != col_type_Link) {
660✔
230
                // Only last column in link chain is allowed to be non-link
6✔
231
                throw InvalidArgument(ErrorCodes::InvalidSortDescriptor, "All but last property must be a link");
12✔
232
            }
12✔
233
            tables[j + 1] = tables[j]->get_link_target(col).unchecked_ptr();
648✔
234
        }
648✔
235

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

273✔
238
        auto& translated_keys = m_columns.back().translated_keys;
546✔
239
        translated_keys.resize(translated_size);
546✔
240

273✔
241
        for (const auto& index : indexes) {
2,646✔
242
            size_t index_in_view = index.index_in_view;
2,646✔
243
            ObjKey translated_key = index.key_for_object;
2,646✔
244
            for (size_t j = 0; j + 1 < sz; ++j) {
5,574✔
245
                const Obj obj = tables[j]->get_object(translated_key);
3,162✔
246
                // type was checked when creating the ColumnsDescriptor
1,581✔
247
                translated_key = columns[j].get_link_target(obj);
3,162✔
248
                if (!translated_key || translated_key.is_unresolved()) {
3,162✔
249
                    translated_key = null_key; // normalize unresolve to null
234✔
250
                    break;
234✔
251
                }
234✔
252
            }
3,162✔
253
            translated_keys[index_in_view] = translated_key;
2,646✔
254
        }
2,646✔
255
    }
546✔
256
    m_cache.resize(column_lists.size() - 1);
19,029✔
257
}
19,017✔
258

259
BaseDescriptor::Sorter DistinctDescriptor::sorter(Table const& table, const IndexPairs& indexes) const
260
{
1,188✔
261
    REALM_ASSERT(!m_column_keys.empty());
1,188✔
262
    std::vector<bool> ascending(m_column_keys.size(), true);
1,188✔
263
    return Sorter(m_column_keys, ascending, table, indexes);
1,188✔
264
}
1,188✔
265

266
void DistinctDescriptor::execute(IndexPairs& v, const Sorter& predicate, const BaseDescriptor* next) const
267
{
1,188✔
268
    using IP = ColumnsDescriptor::IndexPair;
1,188✔
269
    // Remove all rows which have a null link along the way to the distinct columns
594✔
270
    if (predicate.has_links()) {
1,188✔
271
        auto nulls = std::remove_if(v.begin(), v.end(), [&](const IP& index) {
1,026✔
272
            return predicate.any_is_null(index);
1,026✔
273
        });
1,026✔
274
        v.erase(nulls, v.end());
198✔
275
    }
198✔
276

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

594✔
280
    // Move duplicates to the back - "not less than" is "equal" since they're sorted
594✔
281
    auto duplicates = std::unique(v.begin(), v.end(), [&](const IP& a, const IP& b) {
5,382✔
282
        return !predicate(a, b, false);
5,382✔
283
    });
5,382✔
284
    // Erase the duplicates
594✔
285
    v.erase(duplicates, v.end());
1,188✔
286
    bool will_be_sorted_next = next && next->get_type() == DescriptorType::Sort;
1,188✔
287
    if (!will_be_sorted_next) {
1,188✔
288
        // Restore the original order, this is either the original
486✔
289
        // tableview order or the order of the previous sort
486✔
290
        std::sort(v.begin(), v.end(), [](const IP& a, const IP& b) {
3,615✔
291
            return a.index_in_view < b.index_in_view;
3,615✔
292
        });
3,615✔
293
    }
972✔
294
}
1,188✔
295

296
BaseDescriptor::Sorter SortDescriptor::sorter(Table const& table, const IndexPairs& indexes) const
297
{
17,853✔
298
    REALM_ASSERT(!m_column_keys.empty());
17,853✔
299
    return Sorter(m_column_keys, m_ascending, table, indexes);
17,853✔
300
}
17,853✔
301

302
void SortDescriptor::execute(IndexPairs& v, const Sorter& predicate, const BaseDescriptor* next) const
303
{
17,829✔
304
    size_t limit = size_t(-1);
17,829✔
305
    if (next && next->get_type() == DescriptorType::Limit) {
17,829✔
306
        limit = static_cast<const LimitDescriptor*>(next)->get_limit();
1,674✔
307
    }
1,674✔
308
    // Measurements shows that if limit is smaller than size / 16, then
8,919✔
309
    // it is quicker to make a sorted insert into a smaller vector
8,919✔
310
    if (limit < (v.size() >> 4)) {
17,829✔
311
        IndexPairs buffer;
6✔
312
        buffer.reserve(limit + 1);
6✔
313
        for (auto& elem : v) {
60,000✔
314
            auto it = std::lower_bound(buffer.begin(), buffer.end(), elem, std::ref(predicate));
60,000✔
315
            buffer.insert(it, elem);
60,000✔
316
            if (buffer.size() > limit) {
60,000✔
317
                buffer.pop_back();
59,400✔
318
            }
59,400✔
319
        }
60,000✔
320
        v.m_removed_by_limit += v.size() - limit;
6✔
321
        v.erase(v.begin() + limit, v.end());
6✔
322
        std::move(buffer.begin(), buffer.end(), v.begin());
6✔
323
    }
6✔
324
    else {
17,823✔
325
        std::sort(v.begin(), v.end(), std::ref(predicate));
17,823✔
326
    }
17,823✔
327

8,919✔
328
    // not doing this on the last step is an optimisation
8,919✔
329
    if (next) {
17,829✔
330
        const size_t v_size = v.size();
2,016✔
331
        // Distinct must choose the winning unique elements by sorted
1,014✔
332
        // order not by the previous tableview order, the lowest
1,014✔
333
        // "index_in_view" wins.
1,014✔
334
        for (size_t i = 0; i < v_size; ++i) {
6,654✔
335
            v[i].index_in_view = i;
4,638✔
336
        }
4,638✔
337
    }
2,016✔
338
}
17,829✔
339

340
std::string LimitDescriptor::get_description(ConstTableRef) const
341
{
246✔
342
    return "LIMIT(" + util::serializer::print_value(m_limit) + ")";
246✔
343
}
246✔
344

345
std::unique_ptr<BaseDescriptor> LimitDescriptor::clone() const
346
{
5,754✔
347
    return std::unique_ptr<BaseDescriptor>(new LimitDescriptor(*this));
5,754✔
348
}
5,754✔
349

350
void LimitDescriptor::execute(IndexPairs& v, const Sorter&, const BaseDescriptor*) const
351
{
2,022✔
352
    if (v.size() > m_limit) {
2,022✔
353
        v.m_removed_by_limit += v.size() - m_limit;
432✔
354
        v.erase(v.begin() + m_limit, v.end());
432✔
355
    }
432✔
356
}
2,022✔
357

358

359
// This function must conform to 'is less' predicate - that is:
360
// return true if i is strictly smaller than j
361
bool BaseDescriptor::Sorter::operator()(IndexPair i, IndexPair j, bool total_ordering) const
362
{
2,372,823✔
363
    // Sorting can be specified by multiple columns, so that if two entries in the first column are
1,210,236✔
364
    // identical, then the rows are ordered according to the second column, and so forth. For the
1,210,236✔
365
    // first column, all the payload of the View is cached in IndexPair::cached_value.
1,210,236✔
366
    for (size_t t = 0; t < m_columns.size(); t++) {
3,672,411✔
367
        ObjKey key_i = i.key_for_object;
3,325,089✔
368
        ObjKey key_j = j.key_for_object;
3,325,089✔
369

1,685,769✔
370
        if (!m_columns[t].translated_keys.empty()) {
3,325,089✔
371
            key_i = m_columns[t].translated_keys[i.index_in_view];
4,476✔
372
            key_j = m_columns[t].translated_keys[j.index_in_view];
4,476✔
373

2,523✔
374
            bool null_i = !key_i;
4,476✔
375
            bool null_j = !key_j;
4,476✔
376

2,523✔
377
            if (null_i && null_j) {
4,476✔
378
                continue;
126✔
379
            }
126✔
380
            if (null_i || null_j) {
4,350✔
381
                // Sort null links at the end if m_ascending[t], else at beginning.
102✔
382
                return m_columns[t].ascending != null_i;
201✔
383
            }
201✔
384
        }
3,324,762✔
385

1,685,589✔
386
        int c;
3,324,762✔
387

1,685,589✔
388
        if (t == 0) {
3,324,762✔
389
            c = i.cached_value.compare(j.cached_value);
2,372,535✔
390
        }
2,372,535✔
391
        else {
952,227✔
392
            if (m_cache[t - 1].empty()) {
952,227✔
393
                m_cache[t - 1].resize(256);
588✔
394
            }
588✔
395
            ObjCache& cache_i = m_cache[t - 1][key_i.value & 0xFF];
952,227✔
396
            ObjCache& cache_j = m_cache[t - 1][key_j.value & 0xFF];
952,227✔
397

475,506✔
398
            if (cache_i.key != key_i) {
952,227✔
399
                const auto& obj = m_columns[t].table->get_object(key_i);
398,475✔
400
                const auto& col_key = m_columns[t].col_key;
398,475✔
401

164,370✔
402
                cache_i.value = col_key.get_value(obj);
398,475✔
403
                cache_i.key = key_i;
398,475✔
404
            }
398,475✔
405
            Mixed val_i = cache_i.value;
952,227✔
406

475,506✔
407
            if (cache_j.key != key_j) {
952,227✔
408
                const auto& obj = m_columns[t].table->get_object(key_j);
120,525✔
409
                const auto& col_key = m_columns[t].col_key;
120,525✔
410

112,740✔
411
                cache_j.value = col_key.get_value(obj);
120,525✔
412
                cache_j.key = key_j;
120,525✔
413
            }
120,525✔
414

475,506✔
415
            c = val_i.compare(cache_j.value);
952,227✔
416
        }
952,227✔
417
        // if c is negative i comes before j
1,685,589✔
418
        if (c) {
3,324,762✔
419
            return m_columns[t].ascending ? c < 0 : c > 0;
1,889,472✔
420
        }
2,025,300✔
421
    }
3,324,762✔
422
    // make sort stable by using original index as final comparison
1,210,236✔
423
    return total_ordering ? i.index_in_view < j.index_in_view : 0;
1,386,408✔
424
}
2,372,823✔
425

426
void BaseDescriptor::Sorter::cache_first_column(IndexPairs& v)
427
{
21,039✔
428
    if (m_columns.empty())
21,039✔
429
        return;
2,022✔
430

9,513✔
431
    auto& col = m_columns[0];
19,017✔
432
    const auto& ck = col.col_key;
19,017✔
433
    for (size_t i = 0; i < v.size(); i++) {
270,276✔
434
        IndexPair& index = v[i];
251,259✔
435
        ObjKey key = index.key_for_object;
251,259✔
436

125,622✔
437
        if (!col.translated_keys.empty()) {
251,259✔
438
            key = col.translated_keys[v[i].index_in_view];
2,340✔
439
            if (!key) {
2,340✔
440
                index.cached_value = Mixed();
180✔
441
                continue;
180✔
442
            }
180✔
443
        }
251,079✔
444

125,532✔
445
        const auto obj = col.table->get_object(key);
251,079✔
446
        index.cached_value = ck.get_value(obj);
251,079✔
447
    }
251,079✔
448
}
19,017✔
449

450
DescriptorOrdering::DescriptorOrdering(const DescriptorOrdering& other)
451
    : AtomicRefCountBase()
452
{
45,288✔
453
    for (const auto& d : other.m_descriptors) {
28,386✔
454
        m_descriptors.emplace_back(d->clone());
11,460✔
455
    }
11,460✔
456
}
45,288✔
457

458
DescriptorOrdering& DescriptorOrdering::operator=(const DescriptorOrdering& rhs)
459
{
97,860✔
460
    if (&rhs != this) {
97,860✔
461
        m_descriptors.clear();
97,860✔
462
        for (const auto& d : rhs.m_descriptors) {
86,208✔
463
            m_descriptors.emplace_back(d->clone());
73,974✔
464
        }
73,974✔
465
    }
97,860✔
466
    return *this;
97,860✔
467
}
97,860✔
468

469
void DescriptorOrdering::append_sort(SortDescriptor sort, SortDescriptor::MergeMode mode)
470
{
77,253✔
471
    if (!sort.is_valid()) {
77,253✔
472
        return;
41,403✔
473
    }
41,403✔
474
    if (!m_descriptors.empty()) {
35,850✔
475
        if (SortDescriptor* previous_sort = dynamic_cast<SortDescriptor*>(m_descriptors.back().get())) {
858✔
476
            previous_sort->merge(std::move(sort), mode);
102✔
477
            return;
102✔
478
        }
102✔
479
    }
35,748✔
480
    m_descriptors.emplace_back(new SortDescriptor(std::move(sort)));
35,748✔
481
}
35,748✔
482

483
void DescriptorOrdering::append_distinct(DistinctDescriptor distinct)
484
{
2,196✔
485
    if (distinct.is_valid()) {
2,196✔
486
        m_descriptors.emplace_back(new DistinctDescriptor(std::move(distinct)));
2,130✔
487
    }
2,130✔
488
}
2,196✔
489

490
void DescriptorOrdering::append_limit(LimitDescriptor limit)
491
{
4,440✔
492
    if (limit.is_valid()) {
4,440✔
493
        m_descriptors.emplace_back(new LimitDescriptor(std::move(limit)));
4,434✔
494
    }
4,434✔
495
}
4,440✔
496

497
void DescriptorOrdering::append(const DescriptorOrdering& other)
498
{
48✔
499
    for (const auto& d : other.m_descriptors) {
48✔
500
        m_descriptors.emplace_back(d->clone());
48✔
501
    }
48✔
502
}
48✔
503

504
void DescriptorOrdering::append(DescriptorOrdering&& other)
505
{
522✔
506
    std::move(other.m_descriptors.begin(), other.m_descriptors.end(), std::back_inserter(m_descriptors));
522✔
507
    other.m_descriptors.clear();
522✔
508
}
522✔
509

510
DescriptorType DescriptorOrdering::get_type(size_t index) const
511
{
23,010✔
512
    REALM_ASSERT(index < m_descriptors.size());
23,010✔
513
    return m_descriptors[index]->get_type();
23,010✔
514
}
23,010✔
515

516
const BaseDescriptor* DescriptorOrdering::operator[](size_t ndx) const
517
{
31,647✔
518
    if (ndx >= m_descriptors.size()) {
31,647✔
519
        throw OutOfBounds("DescriptorOrdering[]", ndx, m_descriptors.size());
×
520
    }
×
521
    return m_descriptors[ndx].get();
31,647✔
522
}
31,647✔
523

524
bool DescriptorOrdering::will_apply_sort() const
525
{
10,974✔
526
    return std::any_of(m_descriptors.begin(), m_descriptors.end(), [](const std::unique_ptr<BaseDescriptor>& desc) {
9,093✔
527
        REALM_ASSERT(desc->is_valid());
7,212✔
528
        return desc->get_type() == DescriptorType::Sort;
7,212✔
529
    });
7,212✔
530
}
10,974✔
531

532
bool DescriptorOrdering::will_apply_distinct() const
533
{
163,659✔
534
    return std::any_of(m_descriptors.begin(), m_descriptors.end(), [](const std::unique_ptr<BaseDescriptor>& desc) {
95,229✔
535
        REALM_ASSERT(desc->is_valid());
28,704✔
536
        return desc->get_type() == DescriptorType::Distinct;
28,704✔
537
    });
28,704✔
538
}
163,659✔
539

540
bool DescriptorOrdering::will_apply_limit() const
541
{
126✔
542
    return std::any_of(m_descriptors.begin(), m_descriptors.end(), [](const std::unique_ptr<BaseDescriptor>& desc) {
186✔
543
        REALM_ASSERT(desc->is_valid());
186✔
544
        return desc->get_type() == DescriptorType::Limit;
186✔
545
    });
186✔
546
}
126✔
547

548
realm::util::Optional<size_t> DescriptorOrdering::get_min_limit() const
549
{
79,428✔
550
    realm::util::Optional<size_t> min_limit;
79,428✔
551
    for (auto it = m_descriptors.begin(); it != m_descriptors.end(); it++) {
93,744✔
552
        if ((*it)->get_type() == DescriptorType::Limit) {
14,316✔
553
            const LimitDescriptor* limit = static_cast<const LimitDescriptor*>(it->get());
240✔
554
            REALM_ASSERT(limit);
240✔
555
            min_limit = bool(min_limit) ? std::min(*min_limit, limit->get_limit()) : limit->get_limit();
234✔
556
        }
240✔
557
    }
14,316✔
558
    return min_limit;
79,428✔
559
}
79,428✔
560

561
util::Optional<size_t> DescriptorOrdering::remove_all_limits()
562
{
×
563
    size_t min_limit = size_t(-1);
×
564
    for (auto it = m_descriptors.begin(); it != m_descriptors.end();) {
×
565
        if ((*it)->get_type() == DescriptorType::Limit) {
×
566
            const LimitDescriptor* limit = static_cast<const LimitDescriptor*>(it->get());
×
567
            if (limit->get_limit() < min_limit) {
×
568
                min_limit = limit->get_limit();
×
569
            }
×
570
            it = m_descriptors.erase(it);
×
571
        }
×
572
        else {
×
573
            ++it;
×
574
        }
×
575
    }
×
576
    return min_limit == size_t(-1) ? util::none : util::some<size_t>(min_limit);
×
577
}
×
578

579
bool DescriptorOrdering::will_limit_to_zero() const
580
{
84✔
581
    return std::any_of(m_descriptors.begin(), m_descriptors.end(), [](const std::unique_ptr<BaseDescriptor>& desc) {
192✔
582
        REALM_ASSERT(desc.get()->is_valid());
192✔
583
        return (desc->get_type() == DescriptorType::Limit &&
192✔
584
                static_cast<LimitDescriptor*>(desc.get())->get_limit() == 0);
114✔
585
    });
192✔
586
}
84✔
587

588
std::string DescriptorOrdering::get_description(ConstTableRef target_table) const
589
{
528✔
590
    std::string description = "";
528✔
591
    for (auto it = m_descriptors.begin(); it != m_descriptors.end(); ++it) {
1,326✔
592
        REALM_ASSERT_DEBUG(bool(*it));
798✔
593
        description += (*it)->get_description(target_table);
798✔
594
        if (it != m_descriptors.end() - 1) {
798✔
595
            description += " ";
342✔
596
        }
342✔
597
    }
798✔
598
    return description;
528✔
599
}
528✔
600

601
void DescriptorOrdering::collect_dependencies(const Table* table)
602
{
65,364✔
603
    m_dependencies.clear();
65,364✔
604
    for (auto& descr : m_descriptors) {
60,843✔
605
        descr->collect_dependencies(table, m_dependencies);
56,328✔
606
    }
56,328✔
607
}
65,364✔
608

609
void DescriptorOrdering::get_versions(const Group* group, TableVersions& versions) const
610
{
1,982,421✔
611
    for (auto table_key : m_dependencies) {
955,806✔
612
        REALM_ASSERT_DEBUG(group);
576✔
613
        versions.emplace_back(table_key, group->get_table(table_key)->get_content_version());
576✔
614
    }
576✔
615
}
1,982,421✔
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