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

realm / realm-core / 2543

05 Aug 2024 04:04PM UTC coverage: 91.108%. Remained the same
2543

push

Evergreen

web-flow
Only track pending client resets done by the same core version (#7944)

If the previous attempt at performing a client reset was done with a different
core version then we should retry the client reset as the new version may have
fixed a bug that made the previous attempt fail (or may be a downgrade to a
version before when the bug was introduced). This also simplifies the tracking
as it means that we don't need to be able to read trackers created by different
versions.

This also means that we can freely change the schema of the table, which this
takes advantage of to drop the unused primary key and make the error required,
as we never actually stored null and the code reading it would have crashed if
it encountered a null error.

102732 of 181534 branches covered (56.59%)

138 of 153 new or added lines in 10 files covered. (90.2%)

60 existing lines in 12 files now uncovered.

216763 of 237919 relevant lines covered (91.11%)

5749223.09 hits per line

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

94.06
/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/table_view.hpp>
22
#include <realm/db.hpp>
23
#include <realm/util/assert.hpp>
24
#include <realm/list.hpp>
25
#include <realm/dictionary.hpp>
26

27
using namespace realm;
28

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

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

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

53
bool ExtendedColumnKey::is_collection() const
54
{
15,672✔
55
    return m_colkey.is_collection() && !has_index();
15,672✔
56
}
15,672✔
57

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

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

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

94

95
ColumnsDescriptor::ColumnsDescriptor(std::vector<std::vector<ExtendedColumnKey>> column_keys)
96
    : m_column_keys(std::move(column_keys))
8,913✔
97
{
17,826✔
98
}
17,826✔
99

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

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

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

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

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

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

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

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

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

215
    m_columns.reserve(column_lists.size());
14,970✔
216
    for (size_t i = 0; i < column_lists.size(); ++i) {
30,618✔
217
        auto& columns = column_lists[i];
15,672✔
218
        auto sz = columns.size();
15,672✔
219
        REALM_ASSERT_EX(!columns.empty(), i);
15,672✔
220

221
        if (columns.empty()) {
15,672✔
222
            throw InvalidArgument(ErrorCodes::InvalidSortDescriptor, "Missing property");
×
223
        }
×
224
        if (columns.empty() || columns.back().is_collection()) {
15,672✔
225
            throw InvalidArgument(ErrorCodes::InvalidSortDescriptor, "Cannot sort on a collection property");
6✔
226
        }
6✔
227

228
        if (sz == 1) { // no link chain
15,666✔
229
            m_columns.emplace_back(&root_table, columns[0], ascending[i]);
15,102✔
230
            continue;
15,102✔
231
        }
15,102✔
232

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

247
        m_columns.emplace_back(tables.back(), columns.back(), ascending[i]);
546✔
248

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

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

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

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

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

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

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

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

339
    // not doing this on the last step is an optimisation
340
    if (next) {
13,710✔
341
        const size_t v_size = v.size();
564✔
342
        // Distinct must choose the winning unique elements by sorted
343
        // order not by the previous tableview order, the lowest
344
        // "index_in_view" wins.
345
        for (size_t i = 0; i < v_size; ++i) {
4,026✔
346
            v[i].index_in_view = i;
3,462✔
347
        }
3,462✔
348
    }
564✔
349
}
13,710✔
350

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

356
std::unique_ptr<BaseDescriptor> LimitDescriptor::clone() const
357
{
738✔
358
    return std::unique_ptr<BaseDescriptor>(new LimitDescriptor(*this));
738✔
359
}
738✔
360

361
void LimitDescriptor::execute(const Table&, KeyValues& key_values, const BaseDescriptor*) const
362
{
582✔
363
    if (key_values.size() > m_limit) {
582✔
364
        key_values.erase(key_values.begin() + m_limit, key_values.end());
342✔
365
    }
342✔
366
}
582✔
367

368
std::string FilterDescriptor::get_description(ConstTableRef) const
369
{
×
370
    throw SerializationError("Serialization of FilterDescriptor is not supported");
×
371
    return "";
×
372
}
×
373

374
std::unique_ptr<BaseDescriptor> FilterDescriptor::clone() const
375
{
138✔
376
    return std::unique_ptr<BaseDescriptor>(new FilterDescriptor(*this));
138✔
377
}
138✔
378

379
void FilterDescriptor::execute(const Table& table, KeyValues& key_values, const BaseDescriptor*) const
380
{
96✔
381
    KeyValues filtered;
96✔
382
    filtered.create();
96✔
383
    auto sz = key_values.size();
96✔
384
    for (size_t i = 0; i < sz; i++) {
156,222✔
385
        auto key = key_values.get(i);
156,126✔
386
        Obj obj = table.try_get_object(key);
156,126✔
387
        if (obj && m_predicate(obj)) {
156,126✔
388
            filtered.add(key);
1,638✔
389
        }
1,638✔
390
    }
156,126✔
391
    key_values = std::move(filtered);
96✔
392
}
96✔
393

394
// This function must conform to 'is less' predicate - that is:
395
// return true if i is strictly smaller than j
396
bool BaseDescriptor::Sorter::operator()(IndexPair i, IndexPair j, bool total_ordering) const
397
{
2,360,070✔
398
    // Sorting can be specified by multiple columns, so that if two entries in the first column are
399
    // identical, then the rows are ordered according to the second column, and so forth. For the
400
    // first column, all the payload of the View is cached in IndexPair::cached_value.
401
    for (size_t t = 0; t < m_columns.size(); t++) {
3,659,904✔
402
        ObjKey key_i = i.key_for_object;
3,313,242✔
403
        ObjKey key_j = j.key_for_object;
3,313,242✔
404

405
        if (!m_columns[t].translated_keys.empty()) {
3,313,242✔
406
            key_i = m_columns[t].translated_keys[i.index_in_view];
4,476✔
407
            key_j = m_columns[t].translated_keys[j.index_in_view];
4,476✔
408

409
            bool null_i = !key_i;
4,476✔
410
            bool null_j = !key_j;
4,476✔
411

412
            if (null_i && null_j) {
4,476✔
413
                continue;
126✔
414
            }
126✔
415
            if (null_i || null_j) {
4,350✔
416
                // Sort null links at the end if m_ascending[t], else at beginning.
417
                return m_columns[t].ascending != null_i;
201✔
418
            }
201✔
419
        }
4,350✔
420

421
        int c;
3,312,915✔
422

423
        if (t == 0) {
3,312,915✔
424
            c = i.cached_value.compare(j.cached_value);
2,359,785✔
425
        }
2,359,785✔
426
        else {
953,130✔
427
            if (m_cache[t - 1].empty()) {
953,130✔
428
                m_cache[t - 1].resize(256);
588✔
429
            }
588✔
430
            ObjCache& cache_i = m_cache[t - 1][key_i.value & 0xFF];
953,130✔
431
            ObjCache& cache_j = m_cache[t - 1][key_j.value & 0xFF];
953,130✔
432

433
            if (cache_i.key != key_i) {
953,130✔
434
                const auto& obj = m_columns[t].table->get_object(key_i);
385,827✔
435
                const auto& col_key = m_columns[t].col_key;
385,827✔
436

437
                cache_i.value = col_key.get_value(obj);
385,827✔
438
                cache_i.key = key_i;
385,827✔
439
            }
385,827✔
440
            Mixed val_i = cache_i.value;
953,130✔
441

442
            if (cache_j.key != key_j) {
953,130✔
443
                const auto& obj = m_columns[t].table->get_object(key_j);
128,067✔
444
                const auto& col_key = m_columns[t].col_key;
128,067✔
445

446
                cache_j.value = col_key.get_value(obj);
128,067✔
447
                cache_j.key = key_j;
128,067✔
448
            }
128,067✔
449

450
            c = val_i.compare(cache_j.value);
953,130✔
451
        }
953,130✔
452
        // if c is negative i comes before j
453
        if (c) {
3,312,915✔
454
            return m_columns[t].ascending ? c < 0 : c > 0;
2,013,207✔
455
        }
2,013,207✔
456
    }
3,312,915✔
457
    // make sort stable by using original index as final comparison
458
    return total_ordering ? i.index_in_view < j.index_in_view : 0;
346,662✔
459
}
2,360,070✔
460

461
void BaseDescriptor::Sorter::cache_first_column(IndexPairs& v)
462
{
14,946✔
463
    if (m_columns.empty())
14,946✔
464
        return;
×
465

466
    auto& col = m_columns[0];
14,946✔
467
    const auto& ck = col.col_key;
14,946✔
468
    for (size_t i = 0; i < v.size(); i++) {
256,338✔
469
        IndexPair& index = v[i];
241,392✔
470
        ObjKey key = index.key_for_object;
241,392✔
471

472
        if (!col.translated_keys.empty()) {
241,392✔
473
            key = col.translated_keys[v[i].index_in_view];
2,340✔
474
            if (!key) {
2,340✔
475
                index.cached_value = Mixed();
180✔
476
                continue;
180✔
477
            }
180✔
478
        }
2,340✔
479

480
        const auto obj = col.table->get_object(key);
241,212✔
481
        index.cached_value = ck.get_value(obj);
241,212✔
482
    }
241,212✔
483
}
14,946✔
484

485
DescriptorOrdering::DescriptorOrdering(const DescriptorOrdering& other)
486
    : AtomicRefCountBase()
23,076✔
487
{
46,161✔
488
    for (const auto& d : other.m_descriptors) {
46,161✔
489
        m_descriptors.emplace_back(d->clone());
11,586✔
490
    }
11,586✔
491
}
46,161✔
492

493
DescriptorOrdering& DescriptorOrdering::operator=(const DescriptorOrdering& rhs)
494
{
89,124✔
495
    if (&rhs != this) {
89,124✔
496
        m_descriptors.clear();
89,124✔
497
        for (const auto& d : rhs.m_descriptors) {
89,124✔
498
            m_descriptors.emplace_back(d->clone());
47,490✔
499
        }
47,490✔
500
    }
89,124✔
501
    return *this;
89,124✔
502
}
89,124✔
503

504
void DescriptorOrdering::append_sort(SortDescriptor sort, SortDescriptor::MergeMode mode)
505
{
57,111✔
506
    if (!sort.is_valid()) {
57,111✔
507
        return;
41,427✔
508
    }
41,427✔
509
    if (!m_descriptors.empty()) {
15,684✔
510
        if (SortDescriptor* previous_sort = dynamic_cast<SortDescriptor*>(m_descriptors.back().get())) {
870✔
511
            previous_sort->merge(std::move(sort), mode);
102✔
512
            return;
102✔
513
        }
102✔
514
    }
870✔
515
    m_descriptors.emplace_back(new SortDescriptor(std::move(sort)));
15,582✔
516
}
15,582✔
517

518
void DescriptorOrdering::append_distinct(DistinctDescriptor distinct)
519
{
2,208✔
520
    if (distinct.is_valid()) {
2,208✔
521
        m_descriptors.emplace_back(new DistinctDescriptor(std::move(distinct)));
2,142✔
522
    }
2,142✔
523
}
2,208✔
524

525
void DescriptorOrdering::append_limit(LimitDescriptor limit)
526
{
948✔
527
    if (limit.is_valid()) {
948✔
528
        m_descriptors.emplace_back(new LimitDescriptor(std::move(limit)));
942✔
529
    }
942✔
530
}
948✔
531

532
void DescriptorOrdering::append_filter(FilterDescriptor filter)
533
{
42✔
534
    if (filter.is_valid()) {
42✔
535
        m_descriptors.emplace_back(new FilterDescriptor(std::move(filter)));
42✔
536
    }
42✔
537
}
42✔
538

539
void DescriptorOrdering::append(const DescriptorOrdering& other)
540
{
48✔
541
    for (const auto& d : other.m_descriptors) {
48✔
542
        m_descriptors.emplace_back(d->clone());
48✔
543
    }
48✔
544
}
48✔
545

546
void DescriptorOrdering::append(DescriptorOrdering&& other)
547
{
522✔
548
    std::move(other.m_descriptors.begin(), other.m_descriptors.end(), std::back_inserter(m_descriptors));
522✔
549
    other.m_descriptors.clear();
522✔
550
}
522✔
551

552
DescriptorType DescriptorOrdering::get_type(size_t index) const
553
{
1,404✔
554
    REALM_ASSERT(index < m_descriptors.size());
1,404✔
555
    return m_descriptors[index]->get_type();
1,404✔
556
}
1,404✔
557

558
const BaseDescriptor* DescriptorOrdering::operator[](size_t ndx) const
559
{
38,646✔
560
    if (ndx >= m_descriptors.size()) {
38,646✔
561
        throw OutOfBounds("DescriptorOrdering[]", ndx, m_descriptors.size());
×
562
    }
×
563
    return m_descriptors[ndx].get();
38,646✔
564
}
38,646✔
565

566
bool DescriptorOrdering::will_apply_sort() const
567
{
11,736✔
568
    return std::any_of(m_descriptors.begin(), m_descriptors.end(), [](const std::unique_ptr<BaseDescriptor>& desc) {
11,736✔
569
        REALM_ASSERT(desc->is_valid());
7,224✔
570
        return desc->get_type() == DescriptorType::Sort;
7,224✔
571
    });
7,224✔
572
}
11,736✔
573

574
bool DescriptorOrdering::will_apply_distinct() const
575
{
41,487✔
576
    return std::any_of(m_descriptors.begin(), m_descriptors.end(), [](const std::unique_ptr<BaseDescriptor>& desc) {
41,487✔
577
        REALM_ASSERT(desc->is_valid());
28,932✔
578
        return desc->get_type() == DescriptorType::Distinct;
28,932✔
579
    });
28,932✔
580
}
41,487✔
581

582
bool DescriptorOrdering::will_apply_limit() const
583
{
126✔
584
    return std::any_of(m_descriptors.begin(), m_descriptors.end(), [](const std::unique_ptr<BaseDescriptor>& desc) {
186✔
585
        REALM_ASSERT(desc->is_valid());
186✔
586
        return desc->get_type() == DescriptorType::Limit;
186✔
587
    });
186✔
588
}
126✔
589

590
bool DescriptorOrdering::will_apply_filter() const
591
{
15,744✔
592
    return std::any_of(m_descriptors.begin(), m_descriptors.end(), [](const std::unique_ptr<BaseDescriptor>& desc) {
15,744✔
593
        REALM_ASSERT(desc->is_valid());
14,274✔
594
        return desc->get_type() == DescriptorType::Filter;
14,274✔
595
    });
14,274✔
596
}
15,744✔
597

598
realm::util::Optional<size_t> DescriptorOrdering::get_min_limit() const
599
{
15,798✔
600
    realm::util::Optional<size_t> min_limit;
15,798✔
601
    for (auto it = m_descriptors.begin(); it != m_descriptors.end(); it++) {
30,198✔
602
        if ((*it)->get_type() == DescriptorType::Limit) {
14,400✔
603
            const LimitDescriptor* limit = static_cast<const LimitDescriptor*>(it->get());
270✔
604
            REALM_ASSERT(limit);
270✔
605
            min_limit = bool(min_limit) ? std::min(*min_limit, limit->get_limit()) : limit->get_limit();
270✔
606
        }
270✔
607
    }
14,400✔
608
    return min_limit;
15,798✔
609
}
15,798✔
610

611
util::Optional<size_t> DescriptorOrdering::remove_all_limits()
612
{
×
613
    size_t min_limit = size_t(-1);
×
614
    for (auto it = m_descriptors.begin(); it != m_descriptors.end();) {
×
615
        if ((*it)->get_type() == DescriptorType::Limit) {
×
616
            const LimitDescriptor* limit = static_cast<const LimitDescriptor*>(it->get());
×
617
            if (limit->get_limit() < min_limit) {
×
618
                min_limit = limit->get_limit();
×
619
            }
×
620
            it = m_descriptors.erase(it);
×
621
        }
×
622
        else {
×
623
            ++it;
×
624
        }
×
625
    }
×
626
    return min_limit == size_t(-1) ? util::none : util::some<size_t>(min_limit);
×
627
}
×
628

629
bool DescriptorOrdering::will_limit_to_zero() const
630
{
84✔
631
    return std::any_of(m_descriptors.begin(), m_descriptors.end(), [](const std::unique_ptr<BaseDescriptor>& desc) {
192✔
632
        REALM_ASSERT(desc.get()->is_valid());
192✔
633
        return (desc->get_type() == DescriptorType::Limit &&
192✔
634
                static_cast<LimitDescriptor*>(desc.get())->get_limit() == 0);
192✔
635
    });
192✔
636
}
84✔
637

638
std::string DescriptorOrdering::get_description(ConstTableRef target_table) const
639
{
540✔
640
    std::string description = "";
540✔
641
    for (auto it = m_descriptors.begin(); it != m_descriptors.end(); ++it) {
1,338✔
642
        REALM_ASSERT_DEBUG(bool(*it));
798✔
643
        description += (*it)->get_description(target_table);
798✔
644
        if (it != m_descriptors.end() - 1) {
798✔
645
            description += " ";
342✔
646
        }
342✔
647
    }
798✔
648
    return description;
540✔
649
}
540✔
650

651
void DescriptorOrdering::collect_dependencies(const Table* table)
652
{
48,138✔
653
    m_dependencies.clear();
48,138✔
654
    for (auto& descr : m_descriptors) {
48,138✔
655
        descr->collect_dependencies(table, m_dependencies);
29,820✔
656
    }
29,820✔
657
}
48,138✔
658

659
void DescriptorOrdering::get_versions(const Group* group, TableVersions& versions) const
660
{
2,002,296✔
661
    for (auto table_key : m_dependencies) {
2,002,296✔
662
        REALM_ASSERT_DEBUG(group);
558✔
663
        versions.emplace_back(table_key, group->get_table(table_key)->get_content_version());
558✔
664
    }
558✔
665
}
2,002,296✔
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