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

realm / realm-core / jonathan.reams_2947

01 Dec 2023 08:08PM UTC coverage: 91.739% (+0.04%) from 91.695%
jonathan.reams_2947

Pull #7160

Evergreen

jbreams
allow handle_error to decide resumability
Pull Request #7160: Prevent resuming a session that has not been fully shut down

92428 of 169414 branches covered (0.0%)

315 of 349 new or added lines in 14 files covered. (90.26%)

80 existing lines in 14 files now uncovered.

232137 of 253041 relevant lines covered (91.74%)

6882826.18 hits per line

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

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

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

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

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

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

84

85
ColumnsDescriptor::ColumnsDescriptor(std::vector<std::vector<ExtendedColumnKey>> column_keys)
86
    : m_column_keys(std::move(column_keys))
87
{
35,727✔
88
}
35,727✔
89

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

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

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

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

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

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

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

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

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

9,633✔
205
    m_columns.reserve(column_lists.size());
19,245✔
206
    for (size_t i = 0; i < column_lists.size(); ++i) {
39,168✔
207
        auto& columns = column_lists[i];
19,947✔
208
        auto sz = columns.size();
19,947✔
209
        REALM_ASSERT_EX(!columns.empty(), i);
19,947✔
210

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

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

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

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

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

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

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

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

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

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

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

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

9,027✔
329
    // not doing this on the last step is an optimisation
9,027✔
330
    if (next) {
18,033✔
331
        const size_t v_size = v.size();
2,265✔
332
        // Distinct must choose the winning unique elements by sorted
1,140✔
333
        // order not by the previous tableview order, the lowest
1,140✔
334
        // "index_in_view" wins.
1,140✔
335
        for (size_t i = 0; i < v_size; ++i) {
7,164✔
336
            v[i].index_in_view = i;
4,899✔
337
        }
4,899✔
338
    }
2,265✔
339
}
18,033✔
340

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

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

351
void LimitDescriptor::execute(const Table&, KeyValues& key_values, const BaseDescriptor*) const
352
{
2,259✔
353
    if (key_values.size() > m_limit) {
2,259✔
354
        key_values.erase(key_values.begin() + m_limit, key_values.end());
366✔
355
    }
366✔
356
}
2,259✔
357

358
std::string FilterDescriptor::get_description(ConstTableRef) const
359
{
×
360
    throw SerializationError("Serialization of FilterDescriptor is not supported");
×
361
    return "";
×
362
}
×
363

364
std::unique_ptr<BaseDescriptor> FilterDescriptor::clone() const
365
{
138✔
366
    return std::unique_ptr<BaseDescriptor>(new FilterDescriptor(*this));
138✔
367
}
138✔
368

369
void FilterDescriptor::execute(const Table& table, KeyValues& key_values, const BaseDescriptor*) const
370
{
96✔
371
    KeyValues filtered;
96✔
372
    filtered.create();
96✔
373
    auto sz = key_values.size();
96✔
374
    for (size_t i = 0; i < sz; i++) {
156,222✔
375
        auto key = key_values.get(i);
156,126✔
376
        Obj obj = table.try_get_object(key);
156,126✔
377
        if (obj && m_predicate(obj)) {
156,126✔
378
            filtered.add(key);
1,638✔
379
        }
1,638✔
380
    }
156,126✔
381
    key_values = std::move(filtered);
96✔
382
}
96✔
383

384
// This function must conform to 'is less' predicate - that is:
385
// return true if i is strictly smaller than j
386
bool BaseDescriptor::Sorter::operator()(IndexPair i, IndexPair j, bool total_ordering) const
387
{
2,399,382✔
388
    // Sorting can be specified by multiple columns, so that if two entries in the first column are
1,229,907✔
389
    // identical, then the rows are ordered according to the second column, and so forth. For the
1,229,907✔
390
    // first column, all the payload of the View is cached in IndexPair::cached_value.
1,229,907✔
391
    for (size_t t = 0; t < m_columns.size(); t++) {
3,718,158✔
392
        ObjKey key_i = i.key_for_object;
3,368,505✔
393
        ObjKey key_j = j.key_for_object;
3,368,505✔
394

1,717,065✔
395
        if (!m_columns[t].translated_keys.empty()) {
3,368,505✔
396
            key_i = m_columns[t].translated_keys[i.index_in_view];
4,476✔
397
            key_j = m_columns[t].translated_keys[j.index_in_view];
4,476✔
398

2,523✔
399
            bool null_i = !key_i;
4,476✔
400
            bool null_j = !key_j;
4,476✔
401

2,523✔
402
            if (null_i && null_j) {
4,476✔
403
                continue;
126✔
404
            }
126✔
405
            if (null_i || null_j) {
4,350✔
406
                // Sort null links at the end if m_ascending[t], else at beginning.
102✔
407
                return m_columns[t].ascending != null_i;
201✔
408
            }
201✔
409
        }
3,368,178✔
410

1,716,885✔
411
        int c;
3,368,178✔
412

1,716,885✔
413
        if (t == 0) {
3,368,178✔
414
            c = i.cached_value.compare(j.cached_value);
2,399,094✔
415
        }
2,399,094✔
416
        else {
969,084✔
417
            if (m_cache[t - 1].empty()) {
969,084✔
418
                m_cache[t - 1].resize(256);
588✔
419
            }
588✔
420
            ObjCache& cache_i = m_cache[t - 1][key_i.value & 0xFF];
969,084✔
421
            ObjCache& cache_j = m_cache[t - 1][key_j.value & 0xFF];
969,084✔
422

487,134✔
423
            if (cache_i.key != key_i) {
969,084✔
424
                const auto& obj = m_columns[t].table->get_object(key_i);
405,993✔
425
                const auto& col_key = m_columns[t].col_key;
405,993✔
426

172,251✔
427
                cache_i.value = col_key.get_value(obj);
405,993✔
428
                cache_i.key = key_i;
405,993✔
429
            }
405,993✔
430
            Mixed val_i = cache_i.value;
969,084✔
431

487,134✔
432
            if (cache_j.key != key_j) {
969,084✔
433
                const auto& obj = m_columns[t].table->get_object(key_j);
124,212✔
434
                const auto& col_key = m_columns[t].col_key;
124,212✔
435

115,908✔
436
                cache_j.value = col_key.get_value(obj);
124,212✔
437
                cache_j.key = key_j;
124,212✔
438
            }
124,212✔
439

487,134✔
440
            c = val_i.compare(cache_j.value);
969,084✔
441
        }
969,084✔
442
        // if c is negative i comes before j
1,716,885✔
443
        if (c) {
3,368,178✔
444
            return m_columns[t].ascending ? c < 0 : c > 0;
1,909,455✔
445
        }
2,049,528✔
446
    }
3,368,178✔
447
    // make sort stable by using original index as final comparison
1,229,907✔
448
    return total_ordering ? i.index_in_view < j.index_in_view : 0;
1,406,547✔
449
}
2,399,382✔
450

451
void BaseDescriptor::Sorter::cache_first_column(IndexPairs& v)
452
{
19,221✔
453
    if (m_columns.empty())
19,221✔
454
        return;
×
455

9,621✔
456
    auto& col = m_columns[0];
19,221✔
457
    const auto& ck = col.col_key;
19,221✔
458
    for (size_t i = 0; i < v.size(); i++) {
270,708✔
459
        IndexPair& index = v[i];
251,487✔
460
        ObjKey key = index.key_for_object;
251,487✔
461

125,739✔
462
        if (!col.translated_keys.empty()) {
251,487✔
463
            key = col.translated_keys[v[i].index_in_view];
2,340✔
464
            if (!key) {
2,340✔
465
                index.cached_value = Mixed();
180✔
466
                continue;
180✔
467
            }
180✔
468
        }
251,307✔
469

125,649✔
470
        const auto obj = col.table->get_object(key);
251,307✔
471
        index.cached_value = ck.get_value(obj);
251,307✔
472
    }
251,307✔
473
}
19,221✔
474

475
DescriptorOrdering::DescriptorOrdering(const DescriptorOrdering& other)
476
    : AtomicRefCountBase()
477
{
46,071✔
478
    for (const auto& d : other.m_descriptors) {
28,773✔
479
        m_descriptors.emplace_back(d->clone());
11,478✔
480
    }
11,478✔
481
}
46,071✔
482

483
DescriptorOrdering& DescriptorOrdering::operator=(const DescriptorOrdering& rhs)
484
{
102,096✔
485
    if (&rhs != this) {
102,096✔
486
        m_descriptors.clear();
102,096✔
487
        for (const auto& d : rhs.m_descriptors) {
87,480✔
488
            m_descriptors.emplace_back(d->clone());
72,855✔
489
        }
72,855✔
490
    }
102,096✔
491
    return *this;
102,096✔
492
}
102,096✔
493

494
void DescriptorOrdering::append_sort(SortDescriptor sort, SortDescriptor::MergeMode mode)
495
{
75,000✔
496
    if (!sort.is_valid()) {
75,000✔
497
        return;
41,403✔
498
    }
41,403✔
499
    if (!m_descriptors.empty()) {
33,597✔
500
        if (SortDescriptor* previous_sort = dynamic_cast<SortDescriptor*>(m_descriptors.back().get())) {
870✔
501
            previous_sort->merge(std::move(sort), mode);
102✔
502
            return;
102✔
503
        }
102✔
504
    }
33,495✔
505
    m_descriptors.emplace_back(new SortDescriptor(std::move(sort)));
33,495✔
506
}
33,495✔
507

508
void DescriptorOrdering::append_distinct(DistinctDescriptor distinct)
509
{
2,196✔
510
    if (distinct.is_valid()) {
2,196✔
511
        m_descriptors.emplace_back(new DistinctDescriptor(std::move(distinct)));
2,130✔
512
    }
2,130✔
513
}
2,196✔
514

515
void DescriptorOrdering::append_limit(LimitDescriptor limit)
516
{
4,944✔
517
    if (limit.is_valid()) {
4,944✔
518
        m_descriptors.emplace_back(new LimitDescriptor(std::move(limit)));
4,938✔
519
    }
4,938✔
520
}
4,944✔
521

522
void DescriptorOrdering::append_filter(FilterDescriptor filter)
523
{
42✔
524
    if (filter.is_valid()) {
42✔
525
        m_descriptors.emplace_back(new FilterDescriptor(std::move(filter)));
42✔
526
    }
42✔
527
}
42✔
528

529
void DescriptorOrdering::append(const DescriptorOrdering& other)
530
{
48✔
531
    for (const auto& d : other.m_descriptors) {
48✔
532
        m_descriptors.emplace_back(d->clone());
48✔
533
    }
48✔
534
}
48✔
535

536
void DescriptorOrdering::append(DescriptorOrdering&& other)
537
{
522✔
538
    std::move(other.m_descriptors.begin(), other.m_descriptors.end(), std::back_inserter(m_descriptors));
522✔
539
    other.m_descriptors.clear();
522✔
540
}
522✔
541

542
DescriptorType DescriptorOrdering::get_type(size_t index) const
543
{
21,018✔
544
    REALM_ASSERT(index < m_descriptors.size());
21,018✔
545
    return m_descriptors[index]->get_type();
21,018✔
546
}
21,018✔
547

548
const BaseDescriptor* DescriptorOrdering::operator[](size_t ndx) const
549
{
65,865✔
550
    if (ndx >= m_descriptors.size()) {
65,865✔
551
        throw OutOfBounds("DescriptorOrdering[]", ndx, m_descriptors.size());
×
552
    }
×
553
    return m_descriptors[ndx].get();
65,865✔
554
}
65,865✔
555

556
bool DescriptorOrdering::will_apply_sort() const
557
{
11,730✔
558
    return std::any_of(m_descriptors.begin(), m_descriptors.end(), [](const std::unique_ptr<BaseDescriptor>& desc) {
9,477✔
559
        REALM_ASSERT(desc->is_valid());
7,224✔
560
        return desc->get_type() == DescriptorType::Sort;
7,224✔
561
    });
7,224✔
562
}
11,730✔
563

564
bool DescriptorOrdering::will_apply_distinct() const
565
{
172,173✔
566
    return std::any_of(m_descriptors.begin(), m_descriptors.end(), [](const std::unique_ptr<BaseDescriptor>& desc) {
99,507✔
567
        REALM_ASSERT(desc->is_valid());
28,752✔
568
        return desc->get_type() == DescriptorType::Distinct;
28,752✔
569
    });
28,752✔
570
}
172,173✔
571

572
bool DescriptorOrdering::will_apply_limit() const
573
{
126✔
574
    return std::any_of(m_descriptors.begin(), m_descriptors.end(), [](const std::unique_ptr<BaseDescriptor>& desc) {
186✔
575
        REALM_ASSERT(desc->is_valid());
186✔
576
        return desc->get_type() == DescriptorType::Limit;
186✔
577
    });
186✔
578
}
126✔
579

580
bool DescriptorOrdering::will_apply_filter() const
581
{
83,478✔
582
    return std::any_of(m_descriptors.begin(), m_descriptors.end(), [](const std::unique_ptr<BaseDescriptor>& desc) {
48,375✔
583
        REALM_ASSERT(desc->is_valid());
14,226✔
584
        return desc->get_type() == DescriptorType::Filter;
14,226✔
585
    });
14,226✔
586
}
83,478✔
587

588
realm::util::Optional<size_t> DescriptorOrdering::get_min_limit() const
589
{
83,526✔
590
    realm::util::Optional<size_t> min_limit;
83,526✔
591
    for (auto it = m_descriptors.begin(); it != m_descriptors.end(); it++) {
97,866✔
592
        if ((*it)->get_type() == DescriptorType::Limit) {
14,340✔
593
            const LimitDescriptor* limit = static_cast<const LimitDescriptor*>(it->get());
240✔
594
            REALM_ASSERT(limit);
240✔
595
            min_limit = bool(min_limit) ? std::min(*min_limit, limit->get_limit()) : limit->get_limit();
234✔
596
        }
240✔
597
    }
14,340✔
598
    return min_limit;
83,526✔
599
}
83,526✔
600

601
util::Optional<size_t> DescriptorOrdering::remove_all_limits()
602
{
×
603
    size_t min_limit = size_t(-1);
×
604
    for (auto it = m_descriptors.begin(); it != m_descriptors.end();) {
×
605
        if ((*it)->get_type() == DescriptorType::Limit) {
×
606
            const LimitDescriptor* limit = static_cast<const LimitDescriptor*>(it->get());
×
607
            if (limit->get_limit() < min_limit) {
×
608
                min_limit = limit->get_limit();
×
609
            }
×
610
            it = m_descriptors.erase(it);
×
611
        }
×
612
        else {
×
613
            ++it;
×
614
        }
×
615
    }
×
616
    return min_limit == size_t(-1) ? util::none : util::some<size_t>(min_limit);
×
617
}
×
618

619
bool DescriptorOrdering::will_limit_to_zero() const
620
{
84✔
621
    return std::any_of(m_descriptors.begin(), m_descriptors.end(), [](const std::unique_ptr<BaseDescriptor>& desc) {
192✔
622
        REALM_ASSERT(desc.get()->is_valid());
192✔
623
        return (desc->get_type() == DescriptorType::Limit &&
192✔
624
                static_cast<LimitDescriptor*>(desc.get())->get_limit() == 0);
114✔
625
    });
192✔
626
}
84✔
627

628
std::string DescriptorOrdering::get_description(ConstTableRef target_table) const
629
{
540✔
630
    std::string description = "";
540✔
631
    for (auto it = m_descriptors.begin(); it != m_descriptors.end(); ++it) {
1,338✔
632
        REALM_ASSERT_DEBUG(bool(*it));
798✔
633
        description += (*it)->get_description(target_table);
798✔
634
        if (it != m_descriptors.end() - 1) {
798✔
635
            description += " ";
342✔
636
        }
342✔
637
    }
798✔
638
    return description;
540✔
639
}
540✔
640

641
void DescriptorOrdering::collect_dependencies(const Table* table)
642
{
66,426✔
643
    m_dependencies.clear();
66,426✔
644
    for (auto& descr : m_descriptors) {
60,798✔
645
        descr->collect_dependencies(table, m_dependencies);
55,197✔
646
    }
55,197✔
647
}
66,426✔
648

649
void DescriptorOrdering::get_versions(const Group* group, TableVersions& versions) const
650
{
1,979,646✔
651
    for (auto table_key : m_dependencies) {
949,812✔
652
        REALM_ASSERT_DEBUG(group);
558✔
653
        versions.emplace_back(table_key, group->get_table(table_key)->get_content_version());
558✔
654
    }
558✔
655
}
1,979,646✔
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