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

realm / realm-core / 2443

27 Jun 2024 05:43PM UTC coverage: 90.958% (+0.02%) from 90.934%
2443

push

Evergreen

web-flow
Fix compacting a Realm file using the existing encryption key (#7844)

`compact()` stored the existing encryption key in a `std::string`, which
expects a nul-terminated string, not a fixed-size buffer. If the key contained
any nul bytes then the key would be truncated and garbage data would be used as
the encryption key instead, and if it didn't then arbitrarily large additional
amounts of memory would also be copied into the buffer. The tests happened to
always use a nul-terminated string as the key and so worked by coincidence.

102144 of 180396 branches covered (56.62%)

52 of 52 new or added lines in 5 files covered. (100.0%)

68 existing lines in 15 files now uncovered.

214772 of 236123 relevant lines covered (90.96%)

5610465.93 hits per line

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

92.99
/src/realm/table_view.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/table_view.hpp>
20
#include <realm/column_integer.hpp>
21
#include <realm/index_string.hpp>
22
#include <realm/transaction.hpp>
23

24
#include <unordered_set>
25

26
using namespace realm;
27

28
TableView::TableView(TableView& src, Transaction* tr, PayloadPolicy policy_mode)
29
    : m_source_column_key(src.m_source_column_key)
31,383✔
30
{
66,051✔
31
    bool was_in_sync = src.is_in_sync();
66,051✔
32
    if (src.m_query)
66,051✔
33
        m_query = Query(*src.m_query, tr, policy_mode);
65,445✔
34
    m_table = tr->import_copy_of(src.m_table);
66,051✔
35

36
    if (policy_mode == PayloadPolicy::Stay)
66,051✔
37
        was_in_sync = false;
96✔
38

39
    VersionID src_version =
66,051✔
40
        dynamic_cast<Transaction*>(src.m_table->get_parent_group())->get_version_of_current_transaction();
66,051✔
41
    if (src_version != tr->get_version_of_current_transaction())
66,051✔
42
        was_in_sync = false;
18✔
43

44
    m_table = tr->import_copy_of(src.m_table);
66,051✔
45
    m_collection_source = tr->import_copy_of(src.m_collection_source);
66,051✔
46
    if (src.m_source_column_key) {
66,051✔
47
        m_linked_obj = tr->import_copy_of(src.m_linked_obj);
606✔
48
    }
606✔
49

50
    if (was_in_sync)
66,051✔
51
        get_dependencies(m_last_seen_versions);
65,931✔
52

53
    // don't use methods which throw after this point...or m_table_view_key_values will leak
54
    if (policy_mode == PayloadPolicy::Copy && src.m_key_values.is_attached()) {
66,051✔
55
        m_key_values = src.m_key_values;
702✔
56
    }
702✔
57
    else if (policy_mode == PayloadPolicy::Move && src.m_key_values.is_attached())
65,349✔
58
        // Requires that 'src' is a writable object
59
        m_key_values = std::move(src.m_key_values);
65,253✔
60
    else {
96✔
61
        m_key_values.create();
96✔
62
    }
96✔
63
    if (policy_mode == PayloadPolicy::Move) {
66,051✔
64
        src.m_last_seen_versions.clear();
65,253✔
65
    }
65,253✔
66
    m_descriptor_ordering = src.m_descriptor_ordering;
66,051✔
67
    m_limit = src.m_limit;
66,051✔
68
}
66,051✔
69

70
// Aggregates ----------------------------------------------------
71

72
template <typename T, Action AggregateOpType>
73
struct Aggregator {};
74

75
template <typename T>
76
struct Aggregator<T, act_Sum> {
77
    using AggType = typename aggregate_operations::Sum<typename util::RemoveOptional<T>::type>;
78
};
79

80
template <typename T>
81
struct Aggregator<T, act_Average> {
82
    using AggType = typename aggregate_operations::Average<typename util::RemoveOptional<T>::type>;
83
};
84

85
template <typename T>
86
struct Aggregator<T, act_Min> {
87
    using AggType = typename aggregate_operations::Minimum<typename util::RemoveOptional<T>::type>;
88
};
89

90
template <typename T>
91
struct Aggregator<T, act_Max> {
92
    using AggType = typename aggregate_operations::Maximum<typename util::RemoveOptional<T>::type>;
93
};
94

95
template <Action action, typename T>
96
Mixed TableView::aggregate(ColKey column_key, size_t* result_count, ObjKey* return_key) const
97
{
4,230✔
98
    static_assert(action == act_Sum || action == act_Max || action == act_Min || action == act_Average);
4,230✔
99
    REALM_ASSERT(m_table->valid_column(column_key));
4,230!
100

101
    size_t non_nulls = 0;
4,230✔
102
    typename Aggregator<T, action>::AggType agg;
4,230✔
103
    ObjKey last_accumulated_key = null_key;
4,230✔
104
    for (size_t tv_index = 0; tv_index < m_key_values.size(); ++tv_index) {
58,308!
105
        ObjKey key(get_key(tv_index));
54,078✔
106

107
        // skip detached references:
108
        if (key == realm::null_key)
54,078!
109
            continue;
×
110

111
        const Obj obj = m_table->try_get_object(key);
54,078✔
112
        // aggregation must be robust in the face of stale keys:
113
        if (!obj.is_valid())
54,078!
114
            continue;
24✔
115

116
        if (obj.is_null(column_key))
54,054!
117
            continue;
8,028✔
118

119
        auto v = obj.get<T>(column_key);
46,026✔
120
        if (agg.accumulate(v)) {
46,026!
121
            ++non_nulls;
31,269✔
122
            if constexpr (action == act_Min || action == act_Max) {
31,269✔
123
                last_accumulated_key = key;
15,249✔
124
            }
15,249✔
125
        }
31,269✔
126
    }
46,026✔
127

128
    if (result_count)
4,230!
129
        *result_count = non_nulls;
696✔
130

131
    if constexpr (action == act_Max || action == act_Min) {
4,230✔
132
        if (return_key) {
2,532!
133
            *return_key = last_accumulated_key;
2,040✔
134
        }
2,040✔
135
    }
1,266✔
136
    else {
1,698✔
137
        static_cast<void>(last_accumulated_key);
1,698✔
138
        static_cast<void>(return_key);
1,698✔
139
    }
1,698✔
140

141
    if (!agg.is_null()) {
4,230!
142
        return agg.result();
3,354✔
143
    }
3,354✔
144
    if (action == act_Sum) {
876✔
145
        if (std::is_same_v<T, Mixed>) {
×
146
            return Decimal128{0};
×
147
        }
×
148
        return T{};
×
149
    }
×
150
    return Mixed();
876✔
151
}
876✔
152

153
template <typename T>
154
size_t TableView::aggregate_count(ColKey column_key, T count_target) const
155
{
48✔
156
    REALM_ASSERT(m_table->valid_column(column_key));
48!
157

158
    if ((m_key_values.size()) == 0) {
48!
159
        return {};
×
160
    }
×
161

162
    size_t cnt = 0;
48✔
163
    for (size_t tv_index = 0; tv_index < m_key_values.size(); ++tv_index) {
240!
164
        ObjKey key(get_key(tv_index));
192✔
165

166
        // skip detached references:
167
        if (key == realm::null_key)
192!
168
            continue;
×
169

170
        const Obj obj = m_table->try_get_object(key);
192✔
171
        if (!obj.is_valid())
192!
172
            continue;
×
173

174
        auto v = obj.get<T>(column_key);
192✔
175
        if (v == count_target) {
192!
176
            cnt++;
102✔
177
        }
102✔
178
    }
192✔
179

180
    return cnt;
48✔
181
}
48✔
182

183
// Count
184
size_t TableView::count_int(ColKey column_key, int64_t target) const
185
{
12✔
186
    if (m_table->is_nullable(column_key))
12✔
187
        return aggregate_count<util::Optional<int64_t>>(column_key, target);
×
188
    else
12✔
189
        return aggregate_count<int64_t>(column_key, target);
12✔
190
}
12✔
191
size_t TableView::count_float(ColKey column_key, float target) const
192
{
12✔
193
    return aggregate_count<float>(column_key, target);
12✔
194
}
12✔
195
size_t TableView::count_double(ColKey column_key, double target) const
196
{
12✔
197
    return aggregate_count<double>(column_key, target);
12✔
198
}
12✔
199
size_t TableView::count_timestamp(ColKey column_key, Timestamp target) const
200
{
12✔
201
    return aggregate_count<Timestamp>(column_key, target);
12✔
202
}
12✔
203
size_t TableView::count_decimal(ColKey column_key, Decimal128 target) const
204
{
×
205
    return aggregate_count<Decimal128>(column_key, target);
×
206
}
×
207
size_t TableView::count_mixed(ColKey column_key, Mixed target) const
208
{
×
209
    return aggregate_count<Mixed>(column_key, target);
×
210
}
×
211

212
template <Action action>
213
std::optional<Mixed> TableView::aggregate(ColKey column_key, size_t* count, ObjKey* return_key) const
214
{
4,422✔
215
    static_assert(action == act_Sum || action == act_Max || action == act_Min || action == act_Average);
4,422✔
216
    m_table->check_column(column_key);
4,422✔
217
    if (column_key.is_collection()) {
4,422✔
218
        return std::nullopt;
48✔
219
    }
48✔
220

221
    switch (column_key.get_type()) {
4,374✔
222
        case col_type_Int:
1,596✔
223
            if (m_table->is_nullable(column_key))
1,596✔
224
                return aggregate<action, util::Optional<int64_t>>(column_key, count, return_key);
1,470✔
225
            return aggregate<action, int64_t>(column_key, count, return_key);
126✔
226
        case col_type_Float:
1,506✔
227
            return aggregate<action, float>(column_key, count, return_key);
1,506✔
228
        case col_type_Double:
342✔
229
            return aggregate<action, double>(column_key, count, return_key);
342✔
230
        case col_type_Timestamp:
465✔
231
            if constexpr (action == act_Min || action == act_Max) {
858✔
232
                return aggregate<action, Timestamp>(column_key, count, return_key);
786✔
233
            }
786✔
234
            break;
×
235
        case col_type_Decimal:
✔
236
            return aggregate<action, Decimal128>(column_key, count, return_key);
×
237
        case col_type_Mixed:
✔
238
            return aggregate<action, Mixed>(column_key, count, return_key);
×
239
        default:
✔
240
            break;
×
241
    }
4,374✔
242
    return util::none;
72✔
243
}
4,374✔
244

245
util::Optional<Mixed> TableView::min(ColKey column_key, ObjKey* return_key) const
246
{
1,290✔
247
    return aggregate<act_Min>(column_key, nullptr, return_key);
1,290✔
248
}
1,290✔
249

250
util::Optional<Mixed> TableView::max(ColKey column_key, ObjKey* return_key) const
251
{
1,302✔
252
    return aggregate<act_Max>(column_key, nullptr, return_key);
1,302✔
253
}
1,302✔
254

255
util::Optional<Mixed> TableView::sum(ColKey column_key) const
256
{
900✔
257
    return aggregate<act_Sum>(column_key, nullptr, nullptr);
900✔
258
}
900✔
259

260
util::Optional<Mixed> TableView::avg(ColKey column_key, size_t* value_count) const
261
{
930✔
262
    return aggregate<act_Average>(column_key, value_count, nullptr);
930✔
263
}
930✔
264

265
void TableView::to_json(std::ostream& out, JSONOutputMode mode) const
266
{
36✔
267
    // Represent table as list of objects
268
    out << "[";
36✔
269

270
    const size_t row_count = size();
36✔
271
    bool first = true;
36✔
272
    for (size_t r = 0; r < row_count; ++r) {
468✔
273
        if (ObjKey key = get_key(r)) {
432✔
274
            if (first) {
432✔
275
                first = false;
36✔
276
            }
36✔
277
            else {
396✔
278
                out << ",";
396✔
279
            }
396✔
280
            m_table->get_object(key).to_json(out, mode);
432✔
281
        }
432✔
282
    }
432✔
283

284
    out << "]";
36✔
285
}
36✔
286

287
bool TableView::depends_on_deleted_object() const
288
{
42✔
289
    if (m_collection_source) {
42✔
290
        return !m_collection_source->get_owning_obj().is_valid();
12✔
291
    }
12✔
292

293
    if (m_source_column_key && !m_linked_obj.is_valid()) {
30✔
294
        return true;
12✔
295
    }
12✔
296
    else if (m_query && m_query->m_source_table_view) {
18✔
297
        return m_query->m_source_table_view->depends_on_deleted_object();
6✔
298
    }
6✔
299
    return false;
12✔
300
}
30✔
301

302
void TableView::get_dependencies(TableVersions& ret) const
303
{
2,101,905✔
304
    auto table = m_table ? m_table.unchecked_ptr() : nullptr;
2,101,905✔
305
    if (m_source_column_key && m_linked_obj) {
2,101,905✔
306
        // m_source_column_key is set when this TableView was created by Table::get_backlink_view().
307
        if (auto linked_table = m_linked_obj.get_table()) {
2,112✔
308
            ret.emplace_back(linked_table->get_key(), linked_table->get_content_version());
2,112✔
309
        }
2,112✔
310
    }
2,112✔
311
    else if (m_query) {
2,099,793✔
312
        m_query->get_outside_versions(ret);
2,053,704✔
313
    }
2,053,704✔
314
    else {
46,089✔
315
        // This TableView was created by Table::get_distinct_view() or get_sorted_view() on collections
316
        ret.emplace_back(table->get_key(), table->get_content_version());
46,089✔
317
    }
46,089✔
318

319
    // Finally add dependencies from sort/distinct
320
    if (table) {
2,105,433✔
321
        m_descriptor_ordering.get_versions(table->get_parent_group(), ret);
2,050,815✔
322
    }
2,050,815✔
323
}
2,101,905✔
324

325
bool TableView::is_in_sync() const
326
{
357,477✔
327
    return m_table && !has_changed();
357,477✔
328
}
357,477✔
329

330
void TableView::sync_if_needed() const
331
{
224,847✔
332
    if (!is_in_sync()) {
224,847✔
333
        // FIXME: Is this a reasonable handling of constness?
334
        const_cast<TableView*>(this)->do_sync();
59,562✔
335
    }
59,562✔
336
}
224,847✔
337

338
void TableView::update_query(const Query& q)
339
{
12✔
340
    REALM_ASSERT(m_query);
12✔
341
    REALM_ASSERT(m_query->m_table);
12✔
342
    REALM_ASSERT(m_query->m_table == q.m_table);
12✔
343

344
    m_query = q;
12✔
345
    do_sync();
12✔
346
}
12✔
347

348
void TableView::clear()
349
{
6,306✔
350
    m_table.check();
6,306✔
351

352
    // If distinct is applied then removing an object may leave us out of sync
353
    // if there's another object with the same value in the distinct column
354
    // as the removed object
355
    bool sync_to_keep =
6,306✔
356
        m_last_seen_versions == get_dependency_versions() && !m_descriptor_ordering.will_apply_distinct();
6,306✔
357

358
    // Remove all invalid keys
359
    auto it = std::remove_if(m_key_values.begin(), m_key_values.end(), [this](const ObjKey& key) {
2,511,762✔
360
        return !m_table->is_valid(key);
2,511,762✔
361
    });
2,511,762✔
362
    m_key_values.erase(it, m_key_values.end());
6,306✔
363

364
    _impl::TableFriend::batch_erase_objects(*get_parent(), m_key_values); // Throws
6,306✔
365

366
    // It is important to not accidentally bring us in sync, if we were
367
    // not in sync to start with:
368
    if (sync_to_keep)
6,306✔
369
        m_last_seen_versions = get_dependency_versions();
6,288✔
370
}
6,306✔
371

372
void TableView::distinct(ColKey column)
373
{
144✔
374
    distinct(DistinctDescriptor({{column}}));
144✔
375
}
144✔
376

377
/// Remove rows that are duplicated with respect to the column set passed as argument.
378
/// Will keep original sorting order so that you can both have a distinct and sorted view.
379
void TableView::distinct(DistinctDescriptor columns)
380
{
432✔
381
    m_descriptor_ordering.append_distinct(std::move(columns));
432✔
382
    m_descriptor_ordering.collect_dependencies(m_table.unchecked_ptr());
432✔
383

384
    do_sync();
432✔
385
}
432✔
386

387
void TableView::limit(LimitDescriptor lim)
388
{
12✔
389
    m_descriptor_ordering.append_limit(std::move(lim));
12✔
390
    do_sync();
12✔
391
}
12✔
392

393
void TableView::filter(FilterDescriptor filter)
394
{
12✔
395
    m_descriptor_ordering.append_filter(std::move(filter));
12✔
396
    do_sync();
12✔
397
}
12✔
398

399
void TableView::apply_descriptor_ordering(const DescriptorOrdering& new_ordering)
400
{
47,913✔
401
    m_descriptor_ordering = new_ordering;
47,913✔
402
    m_descriptor_ordering.collect_dependencies(m_table.unchecked_ptr());
47,913✔
403

404
    do_sync();
47,913✔
405
}
47,913✔
406

407
std::string TableView::get_descriptor_ordering_description() const
408
{
18✔
409
    return m_descriptor_ordering.get_description(m_table);
18✔
410
}
18✔
411

412
// Sort according to one column
413
void TableView::sort(ColKey column, bool ascending)
414
{
438✔
415
    sort(SortDescriptor({{column}}, {ascending}));
438✔
416
}
438✔
417

418
// Sort according to multiple columns, user specified order on each column
419
void TableView::sort(SortDescriptor order)
420
{
1,008✔
421
    m_descriptor_ordering.append_sort(std::move(order), SortDescriptor::MergeMode::prepend);
1,008✔
422
    m_descriptor_ordering.collect_dependencies(m_table.unchecked_ptr());
1,008✔
423

424
    apply_descriptors(m_descriptor_ordering);
1,008✔
425
}
1,008✔
426

427

428
void TableView::do_sync()
429
{
1,618,866✔
430
    util::CriticalSection cs(m_race_detector);
1,618,866✔
431
    // This TableView can be "born" from 4 different sources:
432
    // - LinkView
433
    // - Query::find_all()
434
    // - Table::get_distinct_view()
435
    // - Table::get_backlink_view()
436
    // Here we sync with the respective source.
437
    m_last_seen_versions.clear();
1,618,866✔
438

439
    if (m_collection_source) {
1,618,866✔
440
        m_key_values.clear();
102✔
441
        auto sz = m_collection_source->size();
102✔
442
        for (size_t i = 0; i < sz; i++) {
330✔
443
            m_key_values.add(m_collection_source->get_key(i));
228✔
444
        }
228✔
445
    }
102✔
446
    else if (m_source_column_key) {
1,618,764✔
447
        m_key_values.clear();
756✔
448
        if (m_table && m_linked_obj.is_valid()) {
756✔
449
            if (m_table->valid_column(m_source_column_key)) { // return empty result, if column has been removed
744✔
450
                ColKey backlink_col = m_table->get_opposite_column(m_source_column_key);
738✔
451
                REALM_ASSERT(backlink_col);
738✔
452
                auto backlinks = m_linked_obj.get_all_backlinks(backlink_col);
738✔
453
                for (auto k : backlinks) {
738✔
454
                    m_key_values.add(k);
708✔
455
                }
708✔
456
            }
738✔
457
        }
744✔
458
    }
756✔
459
    // FIXME: Unimplemented for link to a column
460
    else {
1,618,008✔
461
        REALM_ASSERT(m_query);
1,618,008✔
462
        m_query->m_table.check();
1,618,008✔
463

464
        // valid query, so clear earlier results and reexecute it.
465
        if (m_key_values.is_attached())
1,618,008✔
466
            m_key_values.clear();
1,559,739✔
467
        else
58,269✔
468
            m_key_values.create();
58,269✔
469

470
        if (m_query->m_view)
1,618,008✔
471
            m_query->m_view->sync_if_needed();
1,101✔
472
        size_t limit = m_limit;
1,618,008✔
473
        if (!m_descriptor_ordering.is_empty()) {
1,618,008✔
474
            auto type = m_descriptor_ordering[0]->get_type();
39,699✔
475
            if (type == DescriptorType::Limit) {
39,699✔
476
                size_t l = static_cast<const LimitDescriptor*>(m_descriptor_ordering[0])->get_limit();
138✔
477
                if (l < limit)
138✔
478
                    limit = l;
138✔
479
            }
138✔
480
        }
39,699✔
481
        QueryStateFindAll<std::vector<ObjKey>> st(m_key_values, limit);
1,618,008✔
482
        m_query->do_find_all(st);
1,618,008✔
483
    }
1,618,008✔
484

485
    apply_descriptors(m_descriptor_ordering);
1,618,866✔
486

487
    get_dependencies(m_last_seen_versions);
1,618,866✔
488
}
1,618,866✔
489

490
void TableView::apply_descriptors(const DescriptorOrdering& ordering)
491
{
1,659,789✔
492
    if (ordering.is_empty())
1,659,789✔
493
        return;
1,632,027✔
494
    size_t sz = size();
27,762✔
495
    if (sz == 0)
27,762✔
496
        return;
20,958✔
497

498
    // Gather the current rows into a container we can use std algorithms on
499
    size_t detached_ref_count = 0;
2,147,500,072✔
500
    BaseDescriptor::IndexPairs index_pairs;
2,147,500,072✔
501
    bool using_indexpairs = false;
2,147,500,072✔
502

503
    auto apply_indexpairs = [&] {
2,147,500,072✔
504
        m_key_values.clear();
19,605✔
505
        for (auto& pair : index_pairs) {
183,924✔
506
            m_key_values.add(pair.key_for_object);
183,924✔
507
        }
183,924✔
508
        for (size_t t = 0; t < detached_ref_count; ++t)
19,605✔
509
            m_key_values.add(null_key);
×
510
        using_indexpairs = false;
19,605✔
511
    };
19,605✔
512

513
    auto use_indexpairs = [&] {
2,147,500,072✔
514
        index_pairs.reserve(sz);
19,629✔
515
        index_pairs.clear();
19,629✔
516
        // always put any detached refs at the end of the sort
517
        // FIXME: reconsider if this is the right thing to do
518
        // FIXME: consider specialized implementations in derived classes
519
        // (handling detached refs is not required in linkviews)
520
        for (size_t t = 0; t < sz; t++) {
265,875✔
521
            ObjKey key = get_key(t);
246,246✔
522
            if (m_table->is_valid(key)) {
246,246✔
523
                index_pairs.emplace_back(key, t);
246,246✔
524
            }
246,246✔
UNCOV
525
            else
×
UNCOV
526
                ++detached_ref_count;
×
527
        }
246,246✔
528
        using_indexpairs = true;
19,629✔
529
    };
19,629✔
530

531
    const int num_descriptors = int(ordering.size());
2,147,500,072✔
532
    for (int desc_ndx = 0; desc_ndx < num_descriptors; ++desc_ndx) {
2,147,511,577✔
533
        const BaseDescriptor* base_descr = ordering[desc_ndx];
23,049✔
534
        const BaseDescriptor* next = ((desc_ndx + 1) < num_descriptors) ? ordering[desc_ndx + 1] : nullptr;
23,049✔
535

536
        // Some descriptors, like Sort and Distinct, needs us to gather the current rows
537
        // into a container we can use std algorithms on
538
        if (base_descr->need_indexpair()) {
23,049✔
539
            if (!using_indexpairs) {
20,235✔
540
                use_indexpairs();
19,629✔
541
            }
19,629✔
542

543
            BaseDescriptor::Sorter predicate = base_descr->sorter(*m_table, index_pairs);
20,235✔
544

545
            // Sorting can be specified by multiple columns, so that if two entries in the first column are
546
            // identical, then the rows are ordered according to the second column, and so forth. For the
547
            // first column, we cache all the payload of fields of the view in a std::vector<Mixed>
548
            predicate.cache_first_column(index_pairs);
20,235✔
549

550
            base_descr->execute(index_pairs, predicate, next);
20,235✔
551
        }
20,235✔
552
        else {
2,814✔
553
            if (using_indexpairs) {
2,814✔
554
                apply_indexpairs();
2,586✔
555
            }
2,586✔
556
            base_descr->execute(*m_table, m_key_values, next);
2,814✔
557
            sz = size();
2,814✔
558
        }
2,814✔
559
    }
23,049✔
560
    // Apply the results
561
    if (using_indexpairs) {
2,147,500,072✔
562
        apply_indexpairs();
17,019✔
563
    }
17,019✔
564
}
2,147,500,072✔
565

566
bool TableView::is_in_table_order() const
567
{
3,342✔
568
    if (!m_table) {
3,342✔
569
        return false;
6✔
570
    }
6✔
571
    else if (m_collection_source) {
3,336✔
572
        return false;
6✔
573
    }
6✔
574
    else if (m_source_column_key) {
3,330✔
575
        return false;
6✔
576
    }
6✔
577
    else if (!m_query) {
3,324✔
578
        return false;
66✔
579
    }
66✔
580
    else {
3,258✔
581
        m_query->m_table.check();
3,258✔
582
        return m_query->produces_results_in_table_order() && !m_descriptor_ordering.will_apply_sort();
3,258✔
583
    }
3,258✔
584
}
3,342✔
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