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

realm / realm-core / nicola.cabiddu_1040

26 Sep 2023 05:08PM UTC coverage: 91.056% (-1.9%) from 92.915%
nicola.cabiddu_1040

Pull #6766

Evergreen

nicola-cab
several fixes and final client reset algo for collection in mixed
Pull Request #6766: Client Reset for collections in mixed / nested collections

97128 of 178458 branches covered (0.0%)

1524 of 1603 new or added lines in 5 files covered. (95.07%)

4511 existing lines in 109 files now uncovered.

236619 of 259862 relevant lines covered (91.06%)

7169640.31 hits per line

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

93.75
/src/realm/query.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/query.hpp>
20

21
#include <realm/array.hpp>
22
#include <realm/array_integer_tpl.hpp>
23
#include <realm/transaction.hpp>
24
#include <realm/dictionary.hpp>
25
#include <realm/query_conditions_tpl.hpp>
26
#include <realm/query_engine.hpp>
27
#include <realm/query_expression.hpp>
28
#include <realm/table_view.hpp>
29
#include <realm/set.hpp>
30

31
#include <algorithm>
32

33
using namespace realm;
34

35
Query::Query()
36
{
171,696✔
37
    create();
171,696✔
38
}
171,696✔
39

40
Query::Query(ConstTableRef table, const ObjList& list)
41
    : m_table(table.cast_away_const())
42
    , m_source_collection(list.clone_obj_list())
43
{
6,189✔
44
    m_view = m_source_collection.get();
6,189✔
45
    REALM_ASSERT_DEBUG(m_view);
6,189✔
46
    REALM_ASSERT_DEBUG(list.get_target_table() == m_table);
6,189✔
47
    create();
6,189✔
48
}
6,189✔
49

50
Query::Query(ConstTableRef table, LinkCollectionPtr&& list_ptr)
51
    : m_table(table.cast_away_const())
52
    , m_source_collection(std::move(list_ptr))
53
{
×
54
    m_view = m_source_collection.get();
×
55
    REALM_ASSERT_DEBUG(m_view);
×
56
    REALM_ASSERT_DEBUG(m_view->get_target_table() == m_table);
×
57
    create();
×
58
}
×
59

60
Query::Query(ConstTableRef table, TableView* tv)
61
    : m_table(table.cast_away_const())
62
    , m_view(tv)
63
    , m_source_table_view(tv)
64
{
2,361,717✔
65
    create();
2,361,717✔
66
}
2,361,717✔
67

68
Query::Query(ConstTableRef table, std::unique_ptr<TableView> tv)
69
    : m_table(table.cast_away_const())
70
    , m_view(tv.get())
71
    , m_source_table_view(tv.get())
72
    , m_owned_source_table_view(std::move(tv))
73
{
24✔
74
    create();
24✔
75
}
24✔
76

77
void Query::create()
78
{
2,545,920✔
79
    if (m_table && m_table->is_asymmetric()) {
2,545,920✔
80
        throw IllegalOperation{"Query on ephemeral objects not allowed"};
12✔
81
    }
12✔
82
    m_groups.emplace_back();
2,545,908✔
83
}
2,545,908✔
84

85
Query::Query(const Query& source)
86
    : error_code(source.error_code)
87
    , m_groups(source.m_groups)
88
    , m_table(source.m_table)
89
    , m_ordering(source.m_ordering)
90
{
2,432,466✔
91
    if (source.m_owned_source_table_view) {
2,432,466✔
92
        m_owned_source_table_view = source.m_owned_source_table_view->clone();
18✔
93
        m_source_table_view = m_owned_source_table_view.get();
18✔
94
    }
18✔
95
    else {
2,432,448✔
96
        // FIXME: The lifetime of `m_source_table_view` may be tied to that of `source`, which can easily
1,183,065✔
97
        // turn `m_source_table_view` into a dangling reference.
1,183,065✔
98
        m_source_table_view = source.m_source_table_view;
2,432,448✔
99
        m_source_collection = source.m_source_collection ? source.m_source_collection->clone_obj_list() : nullptr;
2,431,851✔
100
    }
2,432,448✔
101
    if (m_source_table_view) {
2,432,466✔
102
        m_view = m_source_table_view;
240✔
103
    }
240✔
104
    else if (m_source_collection) {
2,432,226✔
105
        m_view = m_source_collection.get();
1,188✔
106
    }
1,188✔
107
}
2,432,466✔
108

109
Query& Query::operator=(const Query& source)
110
{
25,506✔
111
    if (this != &source) {
25,506✔
112
        m_groups = source.m_groups;
25,506✔
113
        m_table = source.m_table;
25,506✔
114

12,540✔
115
        if (source.m_owned_source_table_view) {
25,506✔
116
            m_owned_source_table_view = source.m_owned_source_table_view->clone();
6✔
117
            m_source_table_view = m_owned_source_table_view.get();
6✔
118

3✔
119
            m_source_collection = nullptr;
6✔
120
        }
6✔
121
        else {
25,500✔
122
            // FIXME: The lifetime of `m_source_table_view` may be tied to that of `source`, which can easily
12,537✔
123
            // turn `m_source_table_view` into a dangling reference.
12,537✔
124
            m_source_table_view = source.m_source_table_view;
25,500✔
125
            m_owned_source_table_view = nullptr;
25,500✔
126

12,537✔
127
            m_source_collection = source.m_source_collection ? source.m_source_collection->clone_obj_list() : nullptr;
25,500✔
128
        }
25,500✔
129
        if (m_source_table_view) {
25,506✔
130
            m_view = m_source_table_view;
12✔
131
        }
12✔
132
        else if (m_source_collection) {
25,494✔
133
            m_view = m_source_collection.get();
×
134
        }
×
135
        m_ordering = source.m_ordering;
25,506✔
136
    }
25,506✔
137
    return *this;
25,506✔
138
}
25,506✔
139

140
Query::Query(Query&&) = default;
308,991✔
141
Query& Query::operator=(Query&&) = default;
27,300✔
142

143
Query::~Query() noexcept = default;
5,212,311✔
144

145
Query::Query(const Query* source, Transaction* tr, PayloadPolicy policy)
146
{
114,891✔
147
    if (source->m_source_table_view) {
114,891✔
148
        m_owned_source_table_view = tr->import_copy_of(*source->m_source_table_view, policy);
24✔
149
        m_source_table_view = m_owned_source_table_view.get();
24✔
150
        m_view = m_source_table_view;
24✔
151
    }
24✔
152
    else {
114,867✔
153
        // nothing?
57,345✔
154
    }
114,867✔
155
    if (source->m_source_collection) {
114,891✔
156
        m_source_collection = tr->import_copy_of(source->m_source_collection);
696✔
157
        m_view = m_source_collection.get();
696✔
158
        REALM_ASSERT_DEBUG(m_view);
696✔
159
    }
696✔
160
    m_groups = source->m_groups;
114,891✔
161
    if (source->m_table)
114,891✔
162
        set_table(tr->import_copy_of(source->m_table));
114,891✔
163
    // otherwise: empty query.
57,357✔
164
}
114,891✔
165

166
Query::Query(std::unique_ptr<Expression> expr)
167
    : Query()
168
{
65,646✔
169
    if (auto table = expr->get_base_table())
65,646!
170
        set_table(table.cast_away_const());
64,719✔
171

32,823✔
172
    add_expression_node(std::move(expr));
65,646✔
173
}
65,646✔
174

175
void Query::set_table(TableRef tr)
176
{
180,270✔
177
    if (tr == m_table) {
180,270✔
178
        return;
618✔
179
    }
618✔
180

89,733✔
181
    if (tr->is_asymmetric()) {
179,652✔
182
        throw IllegalOperation{"Query on ephemeral objects not allowed"};
×
183
    }
×
184

89,733✔
185
    m_table = tr;
179,652✔
186
    if (m_table) {
179,652✔
187
        ParentNode* root = root_node();
179,652✔
188
        if (root)
179,652✔
189
            root->set_table(m_table);
53,403✔
190
    }
179,652✔
191
}
179,652✔
192

193

194
void Query::add_expression_node(std::unique_ptr<Expression> expression)
195
{
65,643✔
196
    add_node(std::unique_ptr<ParentNode>(new ExpressionNode(std::move(expression))));
65,643✔
197
}
65,643✔
198

199
// Binary
200
Query& Query::equal(ColKey column_key, BinaryData b, bool case_sensitive)
201
{
5,304✔
202
    if (case_sensitive) {
5,304✔
203
        add_condition<Equal>(column_key, b);
5,304✔
204
    }
5,304✔
205
    else {
×
206
        add_condition<EqualIns>(column_key, b);
×
207
    }
×
208
    return *this;
5,304✔
209
}
5,304✔
210
Query& Query::not_equal(ColKey column_key, BinaryData b, bool case_sensitive)
211
{
876✔
212
    if (case_sensitive) {
876✔
213
        add_condition<NotEqual>(column_key, b);
876✔
214
    }
876✔
215
    else {
×
216
        add_condition<NotEqualIns>(column_key, b);
×
217
    }
×
218
    return *this;
876✔
219
}
876✔
220
Query& Query::begins_with(ColKey column_key, BinaryData b, bool case_sensitive)
221
{
276✔
222
    if (case_sensitive) {
276✔
223
        add_condition<BeginsWith>(column_key, b);
252✔
224
    }
252✔
225
    else {
24✔
226
        add_condition<BeginsWithIns>(column_key, b);
24✔
227
    }
24✔
228
    return *this;
276✔
229
}
276✔
230
Query& Query::ends_with(ColKey column_key, BinaryData b, bool case_sensitive)
231
{
300✔
232
    if (case_sensitive) {
300✔
233
        add_condition<EndsWith>(column_key, b);
276✔
234
    }
276✔
235
    else {
24✔
236
        add_condition<EndsWithIns>(column_key, b);
24✔
237
    }
24✔
238
    return *this;
300✔
239
}
300✔
240
Query& Query::contains(ColKey column_key, BinaryData b, bool case_sensitive)
241
{
288✔
242
    if (case_sensitive) {
288✔
243
        add_condition<Contains>(column_key, b);
264✔
244
    }
264✔
245
    else {
24✔
246
        add_condition<ContainsIns>(column_key, b);
24✔
247
    }
24✔
248
    return *this;
288✔
249
}
288✔
250
Query& Query::like(ColKey column_key, BinaryData b, bool case_sensitive)
251
{
60✔
252
    if (case_sensitive) {
60✔
253
        add_condition<Like>(column_key, b);
36✔
254
    }
36✔
255
    else {
24✔
256
        add_condition<LikeIns>(column_key, b);
24✔
257
    }
24✔
258
    return *this;
60✔
259
}
60✔
260

261

262
namespace {
263

264
REALM_NOINLINE REALM_COLD REALM_NORETURN void throw_type_mismatch_error()
265
{
×
266
    throw LogicError(ErrorCodes::TypeMismatch, "Could not build query");
×
267
}
×
268

269
template <class Node>
270
struct MakeConditionNode {
271
    static std::unique_ptr<ParentNode> make(ColKey col_key, typename Node::TConditionValue value)
272
    {
672,204✔
273
        return std::unique_ptr<ParentNode>{new Node(std::move(value), col_key)};
672,204✔
274
    }
672,204✔
275

276
    static std::unique_ptr<ParentNode> make(ColKey col_key, null)
277
    {
1,962✔
278
        return std::unique_ptr<ParentNode>{new Node(null{}, col_key)};
1,962✔
279
    }
1,962✔
280

281
    template <class T = typename Node::TConditionValue>
282
    static typename std::enable_if<!std::is_same<typename util::RemoveOptional<T>::type, T>::value,
283
                                   std::unique_ptr<ParentNode>>::type
284
    make(ColKey col_key, typename util::RemoveOptional<T>::type value)
285
    {
16,218✔
286
        return std::unique_ptr<ParentNode>{new Node(std::move(value), col_key)};
16,218✔
287
    }
16,218✔
288

289
    template <class T>
290
    REALM_FORCEINLINE static std::unique_ptr<ParentNode> make(ColKey, T&&)
291
    {
×
292
        throw_type_mismatch_error();
×
293
    }
×
294
};
295

296
template <class Cond>
297
struct MakeConditionNode<IntegerNode<ArrayInteger, Cond>> {
298
    static std::unique_ptr<ParentNode> make(ColKey col_key, int64_t value)
299
    {
1,504,305✔
300
        return std::unique_ptr<ParentNode>{new IntegerNode<ArrayInteger, Cond>(std::move(value), col_key)};
1,504,305✔
301
    }
1,504,305✔
302

303
    template <class T>
304
    REALM_FORCEINLINE static std::unique_ptr<ParentNode> make(ColKey, T&&)
305
    {
×
306
        throw_type_mismatch_error();
×
307
    }
×
308
};
309

310
template <class Cond>
311
struct MakeConditionNode<StringNode<Cond>> {
312
    static std::unique_ptr<ParentNode> make(ColKey col_key, StringData value)
313
    {
54,225✔
314
        return std::unique_ptr<ParentNode>{new StringNode<Cond>(std::move(value), col_key)};
54,225✔
315
    }
54,225✔
316

317
    static std::unique_ptr<ParentNode> make(ColKey col_key, null)
318
    {
444✔
319
        return std::unique_ptr<ParentNode>{new StringNode<Cond>(null{}, col_key)};
444✔
320
    }
444✔
321

322
    template <class T>
323
    REALM_FORCEINLINE static std::unique_ptr<ParentNode> make(ColKey, T&&)
324
    {
×
325
        throw_type_mismatch_error();
×
326
    }
×
327
};
328

329
template <class Cond>
330
struct MakeConditionNode<MixedNode<Cond>> {
331
    template <class T>
332
    static std::unique_ptr<ParentNode> make(ColKey col_key, T value)
333
    {
1,110✔
334
        return std::unique_ptr<ParentNode>{new MixedNode<Cond>(Mixed(value), col_key)};
1,110✔
335
    }
1,110✔
336
};
337

338
template <class Cond, class T>
339
std::unique_ptr<ParentNode> make_condition_node(const Table& table, ColKey column_key, T value)
340
{
2,217,369✔
341
    table.check_column(column_key);
2,217,369✔
342
    DataType type = DataType(column_key.get_type());
2,217,369✔
343
    switch (type) {
2,217,369✔
344
        case type_Int: {
1,552,593!
345
            if (column_key.get_attrs().test(col_attr_Nullable)) {
1,552,593!
346
                return MakeConditionNode<IntegerNode<ArrayIntNull, Cond>>::make(column_key, value);
16,560✔
347
            }
16,560✔
348
            else {
1,536,033✔
349
                return MakeConditionNode<IntegerNode<ArrayInteger, Cond>>::make(column_key, value);
1,536,033✔
350
            }
1,536,033✔
351
        }
×
352
        case type_Bool: {
2,070!
353
            return MakeConditionNode<BoolNode<Cond>>::make(column_key, value);
2,070✔
354
        }
×
355
        case type_Float: {
6,060!
356
            return MakeConditionNode<FloatDoubleNode<ArrayFloat, Cond>>::make(column_key, value);
6,060✔
357
        }
×
358
        case type_Double: {
6,432!
359
            return MakeConditionNode<FloatDoubleNode<ArrayDouble, Cond>>::make(column_key, value);
6,432✔
360
        }
×
361
        case type_String: {
54,669!
362
            return MakeConditionNode<StringNode<Cond>>::make(column_key, value);
54,669✔
363
        }
×
364
        case type_Binary: {
7,356!
365
            return MakeConditionNode<BinaryNode<Cond>>::make(column_key, value);
7,356✔
366
        }
×
367
        case type_Timestamp: {
6,060!
368
            return MakeConditionNode<TimestampNode<Cond>>::make(column_key, value);
6,060✔
369
        }
×
370
        case type_Decimal: {
1,164!
371
            return MakeConditionNode<DecimalNode<Cond>>::make(column_key, value);
1,164✔
372
        }
×
373
        case type_ObjectId: {
643,764!
374
            return MakeConditionNode<ObjectIdNode<Cond>>::make(column_key, value);
643,764✔
375
        }
×
376
        case type_Mixed: {
1,110!
377
            return MakeConditionNode<MixedNode<Cond>>::make(column_key, value);
1,110✔
378
        }
×
379
        case type_UUID: {
918!
380
            return MakeConditionNode<UUIDNode<Cond>>::make(column_key, value);
918✔
381
        }
×
382
        case type_Link:
705!
383
        case type_LinkList:
456!
384
            if constexpr (std::is_same_v<T, Mixed> && realm::is_any_v<Cond, Equal, NotEqual>) {
912✔
385
                ObjKey key;
912✔
386
                if (value.is_type(type_Link)) {
912✔
387
                    key = value.template get<ObjKey>();
30✔
388
                }
30✔
389
                else if (value.is_type(type_TypedLink)) {
882✔
390
                    ObjLink link = value.get_link();
858✔
391
                    auto target_table = table.get_link_target(column_key);
858✔
392
                    if (target_table->get_key() != link.get_table_key()) {
858✔
393
                        // This will never match
3✔
394
                        return std::unique_ptr<ParentNode>{new ExpressionNode(std::make_unique<FalseExpression>())};
6✔
395
                    }
6✔
396
                    key = link.get_obj_key();
852✔
397
                }
852✔
398
                return std::unique_ptr<ParentNode>{new LinksToNode<Cond>(column_key, key)};
909✔
399
            }
×
400
            break;
×
401
        default:
×
402
            break;
×
403
    }
×
404
    throw_type_mismatch_error();
×
405
}
×
406

407
template <class Cond>
408
std::unique_ptr<ParentNode> make_size_condition_node(const Table& table, ColKey column_key, int64_t value)
409
{
156✔
410
    table.check_column(column_key);
156✔
411
    DataType type = DataType(column_key.get_type());
156✔
412
    ColumnAttrMask attr = column_key.get_attrs();
156✔
413

78✔
414
    if (attr.test(col_attr_List)) {
156✔
415
        return std::unique_ptr<ParentNode>{new SizeListNode<Cond>(value, column_key)};
144✔
416
    }
144✔
417
    switch (type) {
12✔
418
        case type_String: {
6!
419
            return std::unique_ptr<ParentNode>{new SizeNode<StringData, Cond>(value, column_key)};
6✔
420
        }
×
421
        case type_Binary: {
6!
422
            return std::unique_ptr<ParentNode>{new SizeNode<BinaryData, Cond>(value, column_key)};
6✔
423
        }
×
424
        default: {
×
425
            throw_type_mismatch_error();
×
426
        }
×
427
    }
12✔
428
}
12✔
429

430
} // anonymous namespace
431

432
template <typename TConditionFunction, class T>
433
REALM_FORCEINLINE Query& Query::add_condition(ColKey column_key, T value)
434
{
2,284,860✔
435
    auto node = make_condition_node<TConditionFunction>(*m_table, column_key, value);
2,284,860✔
436
    add_node(std::move(node));
2,284,860✔
437
    return *this;
2,284,860✔
438
}
2,284,860✔
439

440

441
template <typename TConditionFunction>
442
Query& Query::add_size_condition(ColKey column_key, int64_t value)
443
{
156✔
444
    auto node = make_size_condition_node<TConditionFunction>(*m_table, column_key, value);
156✔
445
    add_node(std::move(node));
156✔
446
    return *this;
156✔
447
}
156✔
448

449
// Two column methods, any type
450
Query& Query::equal(ColKey column_key1, ColKey column_key2)
451
{
5,232✔
452
    auto node = std::unique_ptr<ParentNode>(new TwoColumnsNode<Equal>(column_key1, column_key2));
5,232✔
453
    add_node(std::move(node));
5,232✔
454
    return *this;
5,232✔
455
}
5,232✔
456
Query& Query::less(ColKey column_key1, ColKey column_key2)
457
{
3,336✔
458
    auto node = std::unique_ptr<ParentNode>(new TwoColumnsNode<Less>(column_key1, column_key2));
3,336✔
459
    add_node(std::move(node));
3,336✔
460
    return *this;
3,336✔
461
}
3,336✔
462
Query& Query::less_equal(ColKey column_key1, ColKey column_key2)
463
{
3,312✔
464
    auto node = std::unique_ptr<ParentNode>(new TwoColumnsNode<LessEqual>(column_key1, column_key2));
3,312✔
465
    add_node(std::move(node));
3,312✔
466
    return *this;
3,312✔
467
}
3,312✔
468
Query& Query::greater(ColKey column_key1, ColKey column_key2)
469
{
3,390✔
470
    auto node = std::unique_ptr<ParentNode>(new TwoColumnsNode<Greater>(column_key1, column_key2));
3,390✔
471
    add_node(std::move(node));
3,390✔
472
    return *this;
3,390✔
473
}
3,390✔
474
Query& Query::greater_equal(ColKey column_key1, ColKey column_key2)
475
{
3,306✔
476
    auto node = std::unique_ptr<ParentNode>(new TwoColumnsNode<GreaterEqual>(column_key1, column_key2));
3,306✔
477
    add_node(std::move(node));
3,306✔
478
    return *this;
3,306✔
479
}
3,306✔
480
Query& Query::not_equal(ColKey column_key1, ColKey column_key2)
481
{
4,992✔
482
    auto node = std::unique_ptr<ParentNode>(new TwoColumnsNode<NotEqual>(column_key1, column_key2));
4,992✔
483
    add_node(std::move(node));
4,992✔
484
    return *this;
4,992✔
485
}
4,992✔
486

487
// null vs column
488
Query& Query::equal(ColKey column_key, null)
489
{
1,380✔
490
    add_condition<Equal>(column_key, null{});
1,380✔
491
    return *this;
1,380✔
492
}
1,380✔
493

494
Query& Query::not_equal(ColKey column_key, null)
495
{
1,086✔
496
    add_condition<NotEqual>(column_key, null());
1,086✔
497
    return *this;
1,086✔
498
}
1,086✔
499

500
// int constant vs column (we need those because '1234' is ambiguous, can convert to float/double/int64_t)
501
Query& Query::equal(ColKey column_key, int value)
502
{
1,381,827✔
503
    return equal(column_key, static_cast<int64_t>(value));
1,381,827✔
504
}
1,381,827✔
505
Query& Query::not_equal(ColKey column_key, int value)
506
{
2,010✔
507
    return not_equal(column_key, static_cast<int64_t>(value));
2,010✔
508
}
2,010✔
509
Query& Query::greater(ColKey column_key, int value)
510
{
6,564✔
511
    return greater(column_key, static_cast<int64_t>(value));
6,564✔
512
}
6,564✔
513
Query& Query::greater_equal(ColKey column_key, int value)
514
{
186✔
515
    return greater_equal(column_key, static_cast<int64_t>(value));
186✔
516
}
186✔
517
Query& Query::less_equal(ColKey column_key, int value)
518
{
168✔
519
    return less_equal(column_key, static_cast<int64_t>(value));
168✔
520
}
168✔
521
Query& Query::less(ColKey column_key, int value)
522
{
1,413✔
523
    return less(column_key, static_cast<int64_t>(value));
1,413✔
524
}
1,413✔
525
Query& Query::between(ColKey column_key, int from, int to)
526
{
180✔
527
    return between(column_key, static_cast<int64_t>(from), static_cast<int64_t>(to));
180✔
528
}
180✔
529

530
Query& Query::links_to(ColKey origin_column_key, ObjKey target_key)
531
{
12,714✔
532
    add_node(std::unique_ptr<ParentNode>(new LinksToNode<Equal>(origin_column_key, target_key)));
12,714✔
533
    return *this;
12,714✔
534
}
12,714✔
535

536
Query& Query::links_to(ColKey origin_column_key, ObjLink target_link)
537
{
12✔
538
    add_condition<Equal>(origin_column_key, Mixed(target_link));
12✔
539
    return *this;
12✔
540
}
12✔
541

542
Query& Query::links_to(ColKey origin_column, const std::vector<ObjKey>& target_keys)
543
{
288✔
544
    add_node(std::unique_ptr<ParentNode>(new LinksToNode<Equal>(origin_column, target_keys)));
288✔
545
    return *this;
288✔
546
}
288✔
547

548
Query& Query::not_links_to(ColKey origin_column_key, const std::vector<ObjKey>& target_keys)
549
{
144✔
550
    add_node(std::unique_ptr<ParentNode>(new LinksToNode<NotEqual>(origin_column_key, target_keys)));
144✔
551
    return *this;
144✔
552
}
144✔
553

554
// int64 constant vs column
555
Query& Query::equal(ColKey column_key, int64_t value)
556
{
1,467,588✔
557
    add_condition<Equal>(column_key, value);
1,467,588✔
558
    return *this;
1,467,588✔
559
}
1,467,588✔
560
Query& Query::not_equal(ColKey column_key, int64_t value)
561
{
29,901✔
562
    add_condition<NotEqual>(column_key, value);
29,901✔
563
    return *this;
29,901✔
564
}
29,901✔
565
Query& Query::greater(ColKey column_key, int64_t value)
566
{
30,984✔
567
    add_condition<Greater>(column_key, value);
30,984✔
568
    return *this;
30,984✔
569
}
30,984✔
570
Query& Query::greater_equal(ColKey column_key, int64_t value)
571
{
18,336✔
572
    if (value > LLONG_MIN) {
18,336✔
573
        add_condition<Greater>(column_key, value - 1);
18,330✔
574
    }
18,330✔
575
    // field >= LLONG_MIN has no effect
9,492✔
576
    return *this;
18,336✔
577
}
18,336✔
578
Query& Query::less_equal(ColKey column_key, int64_t value)
579
{
7,446✔
580
    if (value < LLONG_MAX) {
7,446✔
581
        add_condition<Less>(column_key, value + 1);
7,440✔
582
    }
7,440✔
583
    // field <= LLONG_MAX has no effect
3,723✔
584
    return *this;
7,446✔
585
}
7,446✔
586
Query& Query::less(ColKey column_key, int64_t value)
587
{
10,671✔
588
    add_condition<Less>(column_key, value);
10,671✔
589
    return *this;
10,671✔
590
}
10,671✔
591
Query& Query::between(ColKey column_key, int64_t from, int64_t to)
592
{
318✔
593
    group();
318✔
594
    greater_equal(column_key, from);
318✔
595
    less_equal(column_key, to);
318✔
596
    end_group();
318✔
597
    return *this;
318✔
598
}
318✔
599
Query& Query::equal(ColKey column_key, bool value)
600
{
1,086✔
601
    add_condition<Equal>(column_key, value);
1,086✔
602
    return *this;
1,086✔
603
}
1,086✔
604
Query& Query::not_equal(ColKey column_key, bool value)
605
{
702✔
606
    add_condition<NotEqual>(column_key, value);
702✔
607
    return *this;
702✔
608
}
702✔
609

610
// ------------- float
611
Query& Query::equal(ColKey column_key, float value)
612
{
1,056✔
613
    return add_condition<Equal>(column_key, value);
1,056✔
614
}
1,056✔
615
Query& Query::not_equal(ColKey column_key, float value)
616
{
870✔
617
    return add_condition<NotEqual>(column_key, value);
870✔
618
}
870✔
619
Query& Query::greater(ColKey column_key, float value)
620
{
948✔
621
    return add_condition<Greater>(column_key, value);
948✔
622
}
948✔
623
Query& Query::greater_equal(ColKey column_key, float value)
624
{
1,002✔
625
    return add_condition<GreaterEqual>(column_key, value);
1,002✔
626
}
1,002✔
627
Query& Query::less_equal(ColKey column_key, float value)
628
{
1,002✔
629
    return add_condition<LessEqual>(column_key, value);
1,002✔
630
}
1,002✔
631
Query& Query::less(ColKey column_key, float value)
632
{
888✔
633
    return add_condition<Less>(column_key, value);
888✔
634
}
888✔
635
Query& Query::between(ColKey column_key, float from, float to)
636
{
168✔
637
    group();
168✔
638
    greater_equal(column_key, from);
168✔
639
    less_equal(column_key, to);
168✔
640
    end_group();
168✔
641
    return *this;
168✔
642
}
168✔
643

644

645
// ------------- double
646
Query& Query::equal(ColKey column_key, double value)
647
{
1,176✔
648
    return add_condition<Equal>(column_key, value);
1,176✔
649
}
1,176✔
650
Query& Query::not_equal(ColKey column_key, double value)
651
{
936✔
652
    return add_condition<NotEqual>(column_key, value);
936✔
653
}
936✔
654
Query& Query::greater(ColKey column_key, double value)
655
{
954✔
656
    return add_condition<Greater>(column_key, value);
954✔
657
}
954✔
658
Query& Query::greater_equal(ColKey column_key, double value)
659
{
1,062✔
660
    return add_condition<GreaterEqual>(column_key, value);
1,062✔
661
}
1,062✔
662
Query& Query::less_equal(ColKey column_key, double value)
663
{
1,038✔
664
    return add_condition<LessEqual>(column_key, value);
1,038✔
665
}
1,038✔
666
Query& Query::less(ColKey column_key, double value)
667
{
972✔
668
    return add_condition<Less>(column_key, value);
972✔
669
}
972✔
670
Query& Query::between(ColKey column_key, double from, double to)
671
{
168✔
672
    group();
168✔
673
    greater_equal(column_key, from);
168✔
674
    less_equal(column_key, to);
168✔
675
    end_group();
168✔
676
    return *this;
168✔
677
}
168✔
678

679

680
// ------------- Timestamp
681
Query& Query::greater(ColKey column_key, Timestamp value)
682
{
924✔
683
    return add_condition<Greater>(column_key, value);
924✔
684
}
924✔
685
Query& Query::equal(ColKey column_key, Timestamp value)
686
{
1,398✔
687
    return add_condition<Equal>(column_key, value);
1,398✔
688
}
1,398✔
689
Query& Query::not_equal(ColKey column_key, Timestamp value)
690
{
876✔
691
    return add_condition<NotEqual>(column_key, value);
876✔
692
}
876✔
693
Query& Query::greater_equal(ColKey column_key, Timestamp value)
694
{
852✔
695
    return add_condition<GreaterEqual>(column_key, value);
852✔
696
}
852✔
697
Query& Query::less_equal(ColKey column_key, Timestamp value)
698
{
852✔
699
    return add_condition<LessEqual>(column_key, value);
852✔
700
}
852✔
701
Query& Query::less(ColKey column_key, Timestamp value)
702
{
876✔
703
    return add_condition<Less>(column_key, value);
876✔
704
}
876✔
705

706
// ------------- ObjectId
707
Query& Query::greater(ColKey column_key, ObjectId value)
708
{
120✔
709
    return add_condition<Greater>(column_key, value);
120✔
710
}
120✔
711
Query& Query::equal(ColKey column_key, ObjectId value)
712
{
643,122✔
713
    return add_condition<Equal>(column_key, value);
643,122✔
714
}
643,122✔
715
Query& Query::not_equal(ColKey column_key, ObjectId value)
716
{
96✔
717
    return add_condition<NotEqual>(column_key, value);
96✔
718
}
96✔
719
Query& Query::greater_equal(ColKey column_key, ObjectId value)
720
{
132✔
721
    return add_condition<GreaterEqual>(column_key, value);
132✔
722
}
132✔
723
Query& Query::less_equal(ColKey column_key, ObjectId value)
724
{
144✔
725
    return add_condition<LessEqual>(column_key, value);
144✔
726
}
144✔
727
Query& Query::less(ColKey column_key, ObjectId value)
728
{
120✔
729
    return add_condition<Less>(column_key, value);
120✔
730
}
120✔
731

732
// ------------- UUID
733
Query& Query::equal(ColKey column_key, UUID value)
734
{
498✔
735
    return add_condition<Equal>(column_key, value);
498✔
736
}
498✔
737
Query& Query::not_equal(ColKey column_key, UUID value)
738
{
108✔
739
    return add_condition<NotEqual>(column_key, value);
108✔
740
}
108✔
741
Query& Query::greater(ColKey column_key, UUID value)
742
{
60✔
743
    return add_condition<Greater>(column_key, value);
60✔
744
}
60✔
745
Query& Query::greater_equal(ColKey column_key, UUID value)
746
{
72✔
747
    return add_condition<GreaterEqual>(column_key, value);
72✔
748
}
72✔
749
Query& Query::less_equal(ColKey column_key, UUID value)
750
{
72✔
751
    return add_condition<LessEqual>(column_key, value);
72✔
752
}
72✔
753
Query& Query::less(ColKey column_key, UUID value)
754
{
60✔
755
    return add_condition<Less>(column_key, value);
60✔
756
}
60✔
757

758
// ------------- Decimal128
759
Query& Query::greater(ColKey column_key, Decimal128 value)
760
{
6✔
761
    return add_condition<Greater>(column_key, value);
6✔
762
}
6✔
763
Query& Query::equal(ColKey column_key, Decimal128 value)
764
{
1,104✔
765
    return add_condition<Equal>(column_key, value);
1,104✔
766
}
1,104✔
767
Query& Query::not_equal(ColKey column_key, Decimal128 value)
768
{
×
769
    return add_condition<NotEqual>(column_key, value);
×
770
}
×
771
Query& Query::greater_equal(ColKey column_key, Decimal128 value)
772
{
18✔
773
    return add_condition<GreaterEqual>(column_key, value);
18✔
774
}
18✔
775
Query& Query::less_equal(ColKey column_key, Decimal128 value)
776
{
24✔
777
    return add_condition<LessEqual>(column_key, value);
24✔
778
}
24✔
779
Query& Query::less(ColKey column_key, Decimal128 value)
780
{
6✔
781
    return add_condition<Less>(column_key, value);
6✔
782
}
6✔
783
Query& Query::between(ColKey column_key, Decimal128 from, Decimal128 to)
784
{
6✔
785
    group();
6✔
786
    greater_equal(column_key, from);
6✔
787
    less_equal(column_key, to);
6✔
788
    end_group();
6✔
789
    return *this;
6✔
790
}
6✔
791

792
// ------------- Mixed
793
Query& Query::greater(ColKey column_key, Mixed value)
794
{
120✔
795
    return add_condition<Greater>(column_key, value);
120✔
796
}
120✔
797
Query& Query::equal(ColKey column_key, Mixed value, bool case_sensitive)
798
{
876✔
799
    if (case_sensitive)
876✔
800
        return add_condition<Equal>(column_key, value);
792✔
801
    else
84✔
802
        return add_condition<EqualIns>(column_key, value);
84✔
803
}
876✔
804
Query& Query::not_equal(ColKey column_key, Mixed value, bool case_sensitive)
805
{
438✔
806
    if (case_sensitive)
438✔
807
        return add_condition<NotEqual>(column_key, value);
378✔
808
    else
60✔
809
        return add_condition<NotEqualIns>(column_key, value);
60✔
810
}
438✔
811
Query& Query::greater_equal(ColKey column_key, Mixed value)
812
{
24✔
813
    return add_condition<GreaterEqual>(column_key, value);
24✔
814
}
24✔
815
Query& Query::less_equal(ColKey column_key, Mixed value)
816
{
24✔
817
    return add_condition<LessEqual>(column_key, value);
24✔
818
}
24✔
819
Query& Query::less(ColKey column_key, Mixed value)
820
{
48✔
821
    return add_condition<Less>(column_key, value);
48✔
822
}
48✔
823
Query& Query::begins_with(ColKey column_key, Mixed value, bool case_sensitive)
824
{
24✔
825
    if (case_sensitive)
24✔
826
        add_condition<BeginsWith>(column_key, value);
12✔
827
    else
12✔
828
        add_condition<BeginsWithIns>(column_key, value);
12✔
829
    return *this;
24✔
830
}
24✔
831
Query& Query::ends_with(ColKey column_key, Mixed value, bool case_sensitive)
832
{
24✔
833
    if (case_sensitive)
24✔
834
        add_condition<EndsWith>(column_key, value);
12✔
835
    else
12✔
836
        add_condition<EndsWithIns>(column_key, value);
12✔
837
    return *this;
24✔
838
}
24✔
839
Query& Query::contains(ColKey column_key, Mixed value, bool case_sensitive)
840
{
48✔
841
    if (case_sensitive)
48✔
842
        add_condition<Contains>(column_key, value);
24✔
843
    else
24✔
844
        add_condition<ContainsIns>(column_key, value);
24✔
845
    return *this;
48✔
846
}
48✔
847
Query& Query::like(ColKey column_key, Mixed value, bool case_sensitive)
848
{
24✔
849
    if (case_sensitive)
24✔
850
        add_condition<Like>(column_key, value);
12✔
851
    else
12✔
852
        add_condition<LikeIns>(column_key, value);
12✔
853
    return *this;
24✔
854
}
24✔
855

856
// ------------- size
857
Query& Query::size_equal(ColKey column_key, int64_t value)
858
{
126✔
859
    return add_size_condition<Equal>(column_key, value);
126✔
860
}
126✔
861
Query& Query::size_not_equal(ColKey column_key, int64_t value)
862
{
6✔
863
    return add_size_condition<NotEqual>(column_key, value);
6✔
864
}
6✔
865
Query& Query::size_greater(ColKey column_key, int64_t value)
866
{
6✔
867
    return add_size_condition<Greater>(column_key, value);
6✔
868
}
6✔
869
Query& Query::size_greater_equal(ColKey column_key, int64_t value)
870
{
6✔
871
    return add_size_condition<GreaterEqual>(column_key, value);
6✔
872
}
6✔
873
Query& Query::size_less_equal(ColKey column_key, int64_t value)
874
{
6✔
875
    return add_size_condition<LessEqual>(column_key, value);
6✔
876
}
6✔
877
Query& Query::size_less(ColKey column_key, int64_t value)
878
{
6✔
879
    return add_size_condition<Less>(column_key, value);
6✔
880
}
6✔
881
Query& Query::size_between(ColKey column_key, int64_t from, int64_t to)
882
{
6✔
883
    group();
6✔
884
    size_greater_equal(column_key, from);
6✔
885
    size_less_equal(column_key, to);
6✔
886
    end_group();
6✔
887
    return *this;
6✔
888
}
6✔
889

890
// Strings, StringData()
891

892
Query& Query::equal(ColKey column_key, StringData value, bool case_sensitive)
893
{
43,017✔
894
    if (case_sensitive)
43,017✔
895
        add_condition<Equal>(column_key, value);
42,045✔
896
    else
972✔
897
        add_condition<EqualIns>(column_key, value);
972✔
898
    return *this;
43,017✔
899
}
43,017✔
900
Query& Query::begins_with(ColKey column_key, StringData value, bool case_sensitive)
901
{
2,076✔
902
    if (case_sensitive)
2,076✔
903
        add_condition<BeginsWith>(column_key, value);
1,206✔
904
    else
870✔
905
        add_condition<BeginsWithIns>(column_key, value);
870✔
906
    return *this;
2,076✔
907
}
2,076✔
908
Query& Query::ends_with(ColKey column_key, StringData value, bool case_sensitive)
909
{
2,094✔
910
    if (case_sensitive)
2,094✔
911
        add_condition<EndsWith>(column_key, value);
1,206✔
912
    else
888✔
913
        add_condition<EndsWithIns>(column_key, value);
888✔
914
    return *this;
2,094✔
915
}
2,094✔
916
Query& Query::contains(ColKey column_key, StringData value, bool case_sensitive)
917
{
2,250✔
918
    if (case_sensitive)
2,250✔
919
        add_condition<Contains>(column_key, value);
1,218✔
920
    else
1,032✔
921
        add_condition<ContainsIns>(column_key, value);
1,032✔
922
    return *this;
2,250✔
923
}
2,250✔
924
Query& Query::not_equal(ColKey column_key, StringData value, bool case_sensitive)
925
{
2,850✔
926
    if (case_sensitive)
2,850✔
927
        add_condition<NotEqual>(column_key, value);
1,998✔
928
    else
852✔
929
        add_condition<NotEqualIns>(column_key, value);
852✔
930
    return *this;
2,850✔
931
}
2,850✔
932
Query& Query::like(ColKey column_key, StringData value, bool case_sensitive)
933
{
2,094✔
934
    if (case_sensitive)
2,094✔
935
        add_condition<Like>(column_key, value);
1,188✔
936
    else
906✔
937
        add_condition<LikeIns>(column_key, value);
906✔
938
    return *this;
2,094✔
939
}
2,094✔
940

941

942
Query& Query::fulltext(ColKey column_key, StringData value)
943
{
312✔
944
    auto index = m_table->get_string_index(column_key);
312✔
945
    if (!(index && index->is_fulltext_index())) {
312✔
946
        throw IllegalOperation{"Column has no fulltext index"};
×
947
    }
×
948

156✔
949
    auto node = std::unique_ptr<ParentNode>{new StringNodeFulltext(std::move(value), column_key)};
312✔
950
    add_node(std::move(node));
312✔
951
    return *this;
312✔
952
}
312✔
953

954
Query& Query::fulltext(ColKey column_key, StringData value, const LinkMap& link_map)
955
{
12✔
956
    auto index = link_map.get_target_table()->get_string_index(column_key);
12✔
957
    if (!(index && index->is_fulltext_index())) {
12✔
958
        throw IllegalOperation{"Column has no fulltext index"};
×
959
    }
×
960

6✔
961
    auto lm = std::make_unique<LinkMap>(link_map);
12✔
962
    auto node = std::unique_ptr<ParentNode>{new StringNodeFulltext(std::move(value), column_key, std::move(lm))};
12✔
963
    add_node(std::move(node));
12✔
964
    return *this;
12✔
965
}
12✔
966

967
// Aggregates =================================================================================
968

969
bool Query::eval_object(const Obj& obj) const
970
{
1,279,593✔
971
    if (has_conditions())
1,279,593✔
972
        return obj && root_node()->match(obj);
1,266,423✔
973

6,669✔
974
    // Query has no conditions, so all rows match, also the user given argument
6,669✔
975
    return true;
13,437✔
976
}
13,437✔
977

978

979
template <typename T>
980
void Query::aggregate(QueryStateBase& st, ColKey column_key) const
981
{
62,682✔
982
    using LeafType = typename ColumnTypeTraits<T>::cluster_leaf_type;
62,682✔
983

31,341✔
984
    if (!has_conditions() && !m_view) {
62,682✔
985
        // use table aggregate
1,806✔
986
        m_table.unchecked_ptr()->aggregate<T>(st, column_key);
3,612✔
987
    }
3,612✔
988
    else {
59,070✔
989

29,535✔
990
        // Aggregate with criteria - goes through the nodes in the query system
29,535✔
991
        init();
59,070✔
992

29,535✔
993
        if (!m_view) {
59,070✔
994
            auto pn = root_node();
55,608✔
995
            auto best = find_best_node(pn);
55,608✔
996
            auto node = pn->m_children[best];
55,608✔
997
            if (node->has_search_index()) {
55,608!
998
                auto keys = node->index_based_keys();
24✔
999
                REALM_ASSERT(keys);
24!
1000
                // The node having the search index can be removed from the query as we know that
12✔
1001
                // all the objects will match this condition
12✔
1002
                pn->m_children[best] = pn->m_children.back();
24✔
1003
                pn->m_children.pop_back();
24✔
1004
                const size_t num_keys = keys->size();
24✔
1005
                for (size_t i = 0; i < num_keys; ++i) {
504!
1006
                    auto obj = m_table->get_object(keys->get(i));
480✔
1007
                    if (pn->m_children.empty() || eval_object(obj)) {
480!
1008
                        st.m_key_offset = obj.get_key().value;
360✔
1009
                        st.match(0, obj.get<T>(column_key));
360✔
1010
                    }
360✔
1011
                }
480✔
1012
            }
24✔
1013
            else {
55,584✔
1014
                // no index, traverse cluster tree
27,792✔
1015
                node = pn;
55,584✔
1016
                LeafType leaf(m_table.unchecked_ptr()->get_alloc());
55,584✔
1017

27,792✔
1018
                auto f = [column_key, &leaf, &node, &st, this](const Cluster* cluster) {
219,024✔
1019
                    size_t e = cluster->node_size();
219,024✔
1020
                    node->set_cluster(cluster);
219,024✔
1021
                    cluster->init_leaf(column_key, &leaf);
219,024✔
1022
                    st.m_key_offset = cluster->get_offset();
219,024✔
1023
                    st.m_key_values = cluster->get_key_array();
219,024✔
1024
                    aggregate_internal(node, &st, 0, e, &leaf);
219,024✔
1025
                    return IteratorControl::AdvanceToNext;
219,024✔
1026
                };
219,024✔
1027

27,792✔
1028
                m_table.unchecked_ptr()->traverse_clusters(f);
55,584✔
1029
            }
55,584✔
1030
        }
55,608✔
1031
        else {
3,462✔
1032
            m_view->for_each([&](const Obj& obj) {
8,520✔
1033
                if (eval_object(obj)) {
8,520✔
1034
                    st.m_key_offset = obj.get_key().value;
8,190✔
1035
                    st.match(0, obj.get<T>(column_key));
8,190✔
1036
                }
8,190✔
1037
                return IteratorControl::AdvanceToNext;
8,520✔
1038
            });
8,520✔
1039
        }
3,462✔
1040
    }
59,070✔
1041
}
62,682✔
1042

1043
size_t Query::find_best_node(ParentNode* pn) const
1044
{
7,767,960✔
1045
    auto score_compare = [](const ParentNode* a, const ParentNode* b) {
3,584,103✔
1046
        return a->cost() < b->cost();
107,742✔
1047
    };
107,742✔
1048
    size_t best = std::distance(pn->m_children.begin(),
7,767,960✔
1049
                                std::min_element(pn->m_children.begin(), pn->m_children.end(), score_compare));
7,767,960✔
1050
    return best;
7,767,960✔
1051
}
7,767,960✔
1052

1053
/**************************************************************************************************************
1054
 *                                                                                                             *
1055
 * Main entry point of a query. Schedules calls to aggregate_local                                             *
1056
 * Return value is the result of the query, or Array pointer for FindAll.                                      *
1057
 *                                                                                                             *
1058
 **************************************************************************************************************/
1059

1060
void Query::aggregate_internal(ParentNode* pn, QueryStateBase* st, size_t start, size_t end,
1061
                               ArrayPayload* source_column) const
1062
{
5,850,207✔
1063
    // Number of matches to find in best condition loop before breaking out to probe other conditions. Too low value
2,508,327✔
1064
    // gives too many constant time overheads everywhere in the query engine. Too high value makes it adapt less
2,508,327✔
1065
    // rapidly to changes in match frequencies.
2,508,327✔
1066
    constexpr size_t findlocals = 64;
5,850,207✔
1067

2,508,327✔
1068
    // Average match distance in linear searches where further increase in distance no longer increases query speed
2,508,327✔
1069
    // (because time spent on handling each match becomes insignificant compared to time spent on the search).
2,508,327✔
1070
    constexpr size_t bestdist = 512;
5,850,207✔
1071

2,508,327✔
1072
    // Minimum number of matches required in a certain condition before it can be used to compute statistics. Too high
2,508,327✔
1073
    // value can spent too much time in a bad node (with high match frequency). Too low value gives inaccurate
2,508,327✔
1074
    // statistics.
2,508,327✔
1075
    constexpr size_t probe_matches = 4;
5,850,207✔
1076

2,508,327✔
1077
    while (start < end) {
12,045,594✔
1078
        // Executes start...end range of a query and will stay inside the condition loop of the node it was called
2,864,253✔
1079
        // on. Can be called on any node; yields same result, but different performance. Returns prematurely if
2,864,253✔
1080
        // condition of called node has evaluated to true local_matches number of times.
2,864,253✔
1081
        // Return value is the next row for resuming aggregating (next row that caller must call aggregate_local on)
2,864,253✔
1082
        size_t best = find_best_node(pn);
6,195,387✔
1083
        start = pn->m_children[best]->aggregate_local(st, start, end, findlocals, source_column);
6,195,387✔
1084
        double current_cost = pn->m_children[best]->cost();
6,195,387✔
1085

2,864,253✔
1086
        // Make remaining conditions compute their m_dD (statistics)
2,864,253✔
1087
        for (size_t c = 0; c < pn->m_children.size() && start < end; c++) {
6,252,576✔
1088
            if (c == best)
16,464✔
1089
                continue;
7,725✔
1090

5,274✔
1091
            // Skip test if there is no way its cost can ever be better than best node's
5,274✔
1092
            if (pn->m_children[c]->m_dT < current_cost) {
8,739✔
1093

5,274✔
1094
                // Limit to bestdist in order not to skip too large parts of index nodes
5,274✔
1095
                size_t maxD = pn->m_children[c]->m_dT == 0.0 ? end - start : bestdist;
8,739✔
1096
                size_t td = pn->m_children[c]->m_dT == 0.0 ? end : (start + maxD > end ? end : start + maxD);
8,739✔
1097
                start = pn->m_children[c]->aggregate_local(st, start, td, probe_matches, source_column);
8,739✔
1098
            }
8,739✔
1099
        }
8,739✔
1100
    }
6,195,387✔
1101
}
5,850,207✔
1102

1103
// Aggregates
1104

1105
std::optional<Mixed> Query::sum(ColKey col_key) const
1106
{
32,580✔
1107
    return AggregateHelper<Query>::sum(*m_table, *this, col_key);
32,580✔
1108
}
32,580✔
1109

1110
std::optional<Mixed> Query::avg(ColKey col_key, size_t* value_count) const
1111
{
26,508✔
1112
    return AggregateHelper<Query>::avg(*m_table, *this, col_key, value_count);
26,508✔
1113
}
26,508✔
1114

1115
std::optional<Mixed> Query::min(ColKey col_key, ObjKey* return_ndx) const
1116
{
2,682✔
1117
    return AggregateHelper<Query>::min(*m_table, *this, col_key, return_ndx);
2,682✔
1118
}
2,682✔
1119

1120
std::optional<Mixed> Query::max(ColKey col_key, ObjKey* return_ndx) const
1121
{
2,664✔
1122
    return AggregateHelper<Query>::max(*m_table, *this, col_key, return_ndx);
2,664✔
1123
}
2,664✔
1124

1125
// Grouping
1126
Query& Query::group()
1127
{
20,394✔
1128
    m_groups.emplace_back();
20,394✔
1129
    return *this;
20,394✔
1130
}
20,394✔
1131
Query& Query::end_group()
1132
{
20,394✔
1133
    if (m_groups.size() < 2) {
20,394✔
1134
        error_code = "Unbalanced group";
6✔
1135
        return *this;
6✔
1136
    }
6✔
1137

10,518✔
1138
    auto end_root_node = std::move(m_groups.back().m_root_node);
20,388✔
1139
    m_groups.pop_back();
20,388✔
1140

10,518✔
1141
    if (end_root_node) {
20,388✔
1142
        add_node(std::move(end_root_node));
20,328✔
1143
    }
20,328✔
1144

10,518✔
1145
    handle_pending_not();
20,388✔
1146
    return *this;
20,388✔
1147
}
20,388✔
1148

1149
// Not creates an implicit group to capture the term that we want to negate.
1150
Query& Query::Not()
1151
{
1,734✔
1152
    group();
1,734✔
1153
    m_groups.back().m_pending_not = true;
1,734✔
1154

867✔
1155
    return *this;
1,734✔
1156
}
1,734✔
1157

1158
// And-terms must end by calling handle_pending_not. This will check if a negation is pending,
1159
// and if so, it will end the implicit group created to hold the term to negate. Note that
1160
// end_group itself will recurse into handle_pending_not if multiple implicit groups are nested
1161
// within each other.
1162
void Query::handle_pending_not()
1163
{
3,060,021✔
1164
    auto& current_group = m_groups.back();
3,060,021✔
1165
    if (m_groups.size() > 1 && current_group.m_pending_not) {
3,060,021✔
1166
        // we are inside group(s) implicitly created to handle a not, so reparent its
867✔
1167
        // nodes into a NotNode (if not empty).
867✔
1168
        current_group.m_pending_not = false;
1,734✔
1169
        if (auto not_root_node = std::move(current_group.m_root_node)) {
1,734✔
1170
            add_node(std::make_unique<NotNode>(std::move(not_root_node)));
1,722✔
1171
        }
1,722✔
1172

867✔
1173
        end_group();
1,734✔
1174
    }
1,734✔
1175
}
3,060,021✔
1176

1177
Query& Query::Or()
1178
{
667,383✔
1179
    auto& current_group = m_groups.back();
667,383✔
1180
    if (current_group.m_state != QueryGroup::State::OrConditionChildren) {
667,383✔
1181
        // Reparent the current group's nodes within an OrNode.
11,565✔
1182
        add_node(std::make_unique<OrNode>(std::move(current_group.m_root_node)));
22,467✔
1183
    }
22,467✔
1184
    current_group.m_state = QueryGroup::State::OrCondition;
667,383✔
1185

334,023✔
1186
    return *this;
667,383✔
1187
}
667,383✔
1188

1189

1190
ObjKey Query::find() const
1191
{
26,130✔
1192
    ObjKey ret;
26,130✔
1193

13,065✔
1194
    if (!m_table)
26,130✔
1195
        return ret;
×
1196

13,065✔
1197
    auto logger = m_table->get_logger();
26,130✔
1198
    bool do_log = false;
26,130✔
1199
    std::chrono::steady_clock::time_point t1;
26,130✔
1200

13,065✔
1201
    if (logger && logger->would_log(util::Logger::Level::debug)) {
26,130✔
1202
        logger->log(util::Logger::Level::debug, "Query find first: '%1'", get_description_safe());
96✔
1203
        t1 = std::chrono::steady_clock::now();
96✔
1204
        do_log = true;
96✔
1205
    }
96✔
1206

13,065✔
1207
    init();
26,130✔
1208

13,065✔
1209
    // ordering could change the way in which objects are returned, in this case we need to run find_all()
13,065✔
1210
    if (m_ordering && (m_ordering->will_apply_sort() || m_ordering->will_apply_distinct())) {
26,130!
1211
        auto table_view = find_all();
18✔
1212
        if (table_view.size() > 0) {
18✔
1213
            // we just need to find the first.
9✔
1214
            ret = table_view.get_key(0);
18✔
1215
        }
18✔
1216
    }
18✔
1217
    else if (!has_conditions()) {
26,112✔
1218
        // User created query with no criteria; return first
24✔
1219
        if (m_view) {
48✔
1220
            if (m_view->size() > 0) {
24✔
1221
                ret = m_view->get_key(0);
12✔
1222
            }
12✔
1223
        }
24✔
1224
        else {
24✔
1225
            ret = m_table->size() == 0 ? null_key : m_table.unchecked_ptr()->begin()->get_key();
18✔
1226
        }
24✔
1227
    }
48✔
1228
    else {
26,064✔
1229
        if (m_view) {
26,064✔
1230
            size_t sz = m_view->size();
168✔
1231
            for (size_t i = 0; i < sz; i++) {
546✔
1232
                const Obj obj = m_view->get_object(i);
516✔
1233
                if (eval_object(obj)) {
516✔
1234
                    ret = obj.get_key();
138✔
1235
                    break;
138✔
1236
                }
138✔
1237
            }
516✔
1238
        }
168✔
1239
        else {
25,896✔
1240
            auto node = root_node();
25,896✔
1241
            ObjKey key;
25,896✔
1242
            auto f = [&node, &key](const Cluster* cluster) {
44,652✔
1243
                size_t end = cluster->node_size();
44,652✔
1244
                node->set_cluster(cluster);
44,652✔
1245
                size_t res = node->find_first(0, end);
44,652✔
1246
                if (res != not_found) {
44,652✔
1247
                    key = cluster->get_real_key(res);
25,596✔
1248
                    // We should just find one - we're done
12,798✔
1249
                    return IteratorControl::Stop;
25,596✔
1250
                }
25,596✔
1251
                return IteratorControl::AdvanceToNext;
19,056✔
1252
            };
19,056✔
1253

12,948✔
1254
            m_table->traverse_clusters(f);
25,896✔
1255
            ret = key;
25,896✔
1256
        }
25,896✔
1257
    }
26,064✔
1258

13,065✔
1259
    if (do_log) {
26,130✔
1260
        auto t2 = std::chrono::steady_clock::now();
96✔
1261
        logger->log(util::Logger::Level::debug, "Query first found: %1, Duration: %2 us", ret,
96✔
1262
                    std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count());
96✔
1263
    }
96✔
1264

13,065✔
1265
    return ret;
26,130✔
1266
}
26,130✔
1267

1268
void Query::do_find_all(QueryStateBase& st) const
1269
{
1,414,941✔
1270
    auto logger = m_table->get_logger();
1,414,941✔
1271
    std::chrono::steady_clock::time_point t1;
1,414,941✔
1272
    bool do_log = false;
1,414,941✔
1273

607,041✔
1274
    if (st.limit() == 0) {
1,414,941✔
1275
        if (logger) {
6✔
1276
            logger->log(util::Logger::Level::debug, "Query find all: limit = 0 -> result: 0");
×
1277
        }
×
1278
        return;
6✔
1279
    }
6✔
1280

607,038✔
1281
    if (logger && logger->would_log(util::Logger::Level::debug)) {
1,414,935✔
1282
        logger->log(util::Logger::Level::debug, "Query find all: '%1', limit = %2", get_description_safe(),
140,106✔
1283
                    int64_t(st.limit()));
140,106✔
1284
        t1 = std::chrono::steady_clock::now();
140,106✔
1285
        do_log = true;
140,106✔
1286
    }
140,106✔
1287

607,038✔
1288
    init();
1,414,935✔
1289

607,038✔
1290
    bool has_cond = has_conditions();
1,414,935✔
1291

607,038✔
1292
    if (m_view) {
1,414,935✔
1293
        size_t sz = m_view->size();
1,281✔
1294
        for (size_t t = 0; t < sz; t++) {
225,996✔
1295
            const Obj obj = m_view->get_object(t);
224,715✔
1296
            if (eval_object(obj)) {
224,715✔
1297
                st.m_key_offset = obj.get_key().value;
76,272✔
1298
                if (!st.match(0, Mixed()))
76,272✔
1299
                    break;
×
1300
            }
76,272✔
1301
        }
224,715✔
1302
    }
1,281✔
1303
    else {
1,413,654✔
1304
        if (!has_cond) {
1,413,654✔
1305
            auto f = [&st](const Cluster* cluster) {
164,535✔
1306
                size_t sz = cluster->node_size();
164,535✔
1307
                st.m_key_offset = cluster->get_offset();
164,535✔
1308
                st.m_key_values = cluster->get_key_array();
164,535✔
1309
                for (size_t i = 0; i < sz; i++) {
27,811,119✔
1310
                    if (!st.match(i, Mixed()))
27,646,632✔
1311
                        return IteratorControl::Stop;
48✔
1312
                }
27,646,632✔
1313
                return IteratorControl::AdvanceToNext;
164,511✔
1314
            };
164,535✔
1315

39,771✔
1316
            m_table->traverse_clusters(f);
79,983✔
1317
        }
79,983✔
1318
        else {
1,333,671✔
1319
            auto pn = root_node();
1,333,671✔
1320
            auto best = find_best_node(pn);
1,333,671✔
1321
            auto node = pn->m_children[best];
1,333,671✔
1322
            if (node->has_search_index()) {
1,333,671✔
1323
                auto keys = node->index_based_keys();
12,138✔
1324
                REALM_ASSERT(keys);
12,138✔
1325

6,069✔
1326
                // The node having the search index can be removed from the query as we know that
6,069✔
1327
                // all the objects will match this condition
6,069✔
1328
                pn->m_children[best] = pn->m_children.back();
12,138✔
1329
                pn->m_children.pop_back();
12,138✔
1330

6,069✔
1331
                const size_t num_keys = keys->size();
12,138✔
1332
                for (size_t i = 0; i < num_keys; ++i) {
247,611✔
1333
                    ObjKey key = keys->get(i);
235,485✔
1334
                    st.m_key_offset = key.value;
235,485✔
1335
                    if (pn->m_children.empty()) {
235,485✔
1336
                        // No more conditions - just add key
98,859✔
1337
                        if (!st.match(0, Mixed()))
197,646✔
1338
                            break;
6✔
1339
                    }
37,839✔
1340
                    else {
37,839✔
1341
                        auto obj = m_table->get_object(key);
37,839✔
1342
                        if (eval_object(obj)) {
37,839✔
1343
                            if (!st.match(0, Mixed()))
19,023✔
1344
                                break;
6✔
1345
                        }
19,023✔
1346
                    }
37,839✔
1347
                }
235,485✔
1348
            }
12,138✔
1349
            else {
1,321,533✔
1350
                // no index on best node (and likely no index at all), descend B+-tree
560,559✔
1351
                node = pn;
1,321,533✔
1352

560,559✔
1353
                auto f = [&node, &st, this](const Cluster* cluster) {
5,621,673✔
1354
                    size_t e = cluster->node_size();
5,621,673✔
1355
                    node->set_cluster(cluster);
5,621,673✔
1356
                    st.m_key_offset = cluster->get_offset();
5,621,673✔
1357
                    st.m_key_values = cluster->get_key_array();
5,621,673✔
1358
                    aggregate_internal(node, &st, 0, e, nullptr);
5,621,673✔
1359
                    // Stop if limit is reached
2,683,665✔
1360
                    return st.match_count() == st.limit() ? IteratorControl::Stop : IteratorControl::AdvanceToNext;
5,621,508✔
1361
                };
5,621,673✔
1362

560,559✔
1363
                m_table->traverse_clusters(f);
1,321,533✔
1364
            }
1,321,533✔
1365
        }
1,333,671✔
1366
    }
1,413,654✔
1367

607,038✔
1368

607,038✔
1369
    if (do_log) {
1,414,935✔
1370
        auto t2 = std::chrono::steady_clock::now();
140,106✔
1371
        logger->log(util::Logger::Level::debug, "Query found: %1, Duration: %2 us", st.match_count(),
140,106✔
1372
                    std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count());
140,106✔
1373
    }
140,106✔
1374
}
1,414,935✔
1375

1376
TableView Query::find_all(size_t limit) const
1377
{
1,465,308✔
1378
    TableView ret(*this, limit);
1,465,308✔
1379
    if (m_ordering) {
1,465,308✔
1380
        // apply_descriptor_ordering will call do_sync
207✔
1381
        ret.apply_descriptor_ordering(*m_ordering);
414✔
1382
    }
414✔
1383
    else {
1,464,894✔
1384
        ret.do_sync();
1,464,894✔
1385
    }
1,464,894✔
1386
    return ret;
1,465,308✔
1387
}
1,465,308✔
1388

1389

1390
size_t Query::do_count(size_t limit) const
1391
{
319,542✔
1392
    auto logger = m_table->get_logger();
319,542✔
1393
    std::chrono::steady_clock::time_point t1;
319,542✔
1394
    bool do_log = false;
319,542✔
1395

159,102✔
1396
    if (limit == 0) {
319,542✔
1397
        if (logger) {
×
1398
            logger->log(util::Logger::Level::debug, "Query count: limit = 0 -> result: 0");
×
1399
        }
×
1400
        return 0;
×
1401
    }
×
1402

159,102✔
1403
    if (!has_conditions()) {
319,542✔
1404
        // User created query with no criteria; count all
7,332✔
1405
        size_t cnt_all;
14,664✔
1406
        if (m_view) {
14,664✔
1407
            cnt_all = std::min(m_view->size(), limit);
102✔
1408
        }
102✔
1409
        else {
14,562✔
1410
            cnt_all = std::min(m_table->size(), limit);
14,562✔
1411
        }
14,562✔
1412
        if (logger) {
14,664✔
1413
            logger->log(util::Logger::Level::debug, "Query count (no condition): limit = %1 -> result: %2",
14,532✔
1414
                        int64_t(limit), cnt_all);
14,532✔
1415
        }
14,532✔
1416
        return cnt_all;
14,664✔
1417
    }
14,664✔
1418

151,770✔
1419
    if (logger && logger->would_log(util::Logger::Level::debug)) {
304,878✔
1420
        logger->log(util::Logger::Level::debug, "Query count: '%1', limit = %2", get_description_safe(),
65,898✔
1421
                    int64_t(limit));
65,898✔
1422
        t1 = std::chrono::steady_clock::now();
65,898✔
1423
        do_log = true;
65,898✔
1424
    }
65,898✔
1425
    size_t cnt = 0;
304,878✔
1426

151,770✔
1427
    init();
304,878✔
1428

151,770✔
1429
    if (m_view) {
304,878✔
1430
        m_view->for_each([&](const Obj& obj) {
969,372✔
1431
            if (eval_object(obj)) {
969,372✔
1432
                cnt++;
483,696✔
1433
            }
483,696✔
1434
            return IteratorControl::AdvanceToNext;
969,372✔
1435
        });
969,372✔
1436
    }
420✔
1437
    else {
304,458✔
1438
        auto pn = root_node();
304,458✔
1439
        auto best = find_best_node(pn);
304,458✔
1440
        auto node = pn->m_children[best];
304,458✔
1441
        if (node->has_search_index()) {
304,458✔
1442
            auto keys = node->index_based_keys();
1,800✔
1443
            REALM_ASSERT(keys);
1,800✔
1444
            if (pn->m_children.size() > 1) {
1,800✔
1445
                // The node having the search index can be removed from the query as we know that
42✔
1446
                // all the objects will match this condition
42✔
1447
                pn->m_children[best] = pn->m_children.back();
84✔
1448
                pn->m_children.pop_back();
84✔
1449
                const size_t num_keys = keys->size();
84✔
1450
                for (size_t i = 0; i < num_keys; ++i) {
28,617✔
1451
                    auto obj = m_table->get_object(keys->get(i));
28,533✔
1452
                    if (eval_object(obj)) {
28,533✔
1453
                        ++cnt;
14,061✔
1454
                        if (cnt == limit)
14,061✔
1455
                            break;
×
1456
                    }
14,061✔
1457
                }
28,533✔
1458
            }
84✔
1459
            else {
1,716✔
1460
                // The node having the search index is the only node
858✔
1461
                auto sz = keys->size();
1,716✔
1462
                cnt = std::min(limit, sz);
1,716✔
1463
            }
1,716✔
1464
        }
1,800✔
1465
        else {
302,658✔
1466
            // no index, descend down the B+-tree instead
150,660✔
1467
            node = pn;
302,658✔
1468
            QueryStateCount st(limit);
302,658✔
1469

150,660✔
1470
            auto f = [&node, &st, this](const Cluster* cluster) {
528,357✔
1471
                size_t e = cluster->node_size();
528,357✔
1472
                node->set_cluster(cluster);
528,357✔
1473
                st.m_key_offset = cluster->get_offset();
528,357✔
1474
                st.m_key_values = cluster->get_key_array();
528,357✔
1475
                aggregate_internal(node, &st, 0, e, nullptr);
528,357✔
1476
                // Stop if limit or end is reached
262,125✔
1477
                return st.match_count() == st.limit() ? IteratorControl::Stop : IteratorControl::AdvanceToNext;
528,342✔
1478
            };
528,357✔
1479

150,660✔
1480
            m_table->traverse_clusters(f);
302,658✔
1481

150,660✔
1482
            cnt = st.get_count();
302,658✔
1483
        }
302,658✔
1484
    }
304,458✔
1485

151,770✔
1486
    if (do_log) {
304,878✔
1487
        auto t2 = std::chrono::steady_clock::now();
65,898✔
1488
        logger->log(util::Logger::Level::debug, "Query matches: %1, Duration: %2 us", cnt,
65,898✔
1489
                    std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count());
65,898✔
1490
    }
65,898✔
1491

151,770✔
1492
    return cnt;
304,878✔
1493
}
304,878✔
1494

1495
size_t Query::count() const
1496
{
239,103✔
1497
    if (!m_table)
239,103✔
1498
        return 0;
×
1499
    return do_count();
239,103✔
1500
}
239,103✔
1501

1502
TableView Query::find_all(const DescriptorOrdering& descriptor) const
1503
{
54,330✔
1504
    if (descriptor.is_empty()) {
54,330✔
1505
        return find_all();
31,212✔
1506
    }
31,212✔
1507

11,892✔
1508
    const size_t default_limit = size_t(-1);
23,118✔
1509

11,892✔
1510
    bool only_limit = true;
23,118✔
1511
    size_t min_limit = size_t(-1);
23,118✔
1512
    for (size_t i = 0; i < descriptor.size(); ++i) {
23,196✔
1513
        if (descriptor.get_type(i) != DescriptorType::Limit) {
23,154✔
1514
            only_limit = false;
23,076✔
1515
            break;
23,076✔
1516
        }
23,076✔
1517
        else {
78✔
1518
            REALM_ASSERT(dynamic_cast<const LimitDescriptor*>(descriptor[i]));
78✔
1519
            const LimitDescriptor* limit = static_cast<const LimitDescriptor*>(descriptor[i]);
78✔
1520
            min_limit = std::min(min_limit, limit->get_limit());
78✔
1521
        }
78✔
1522
    }
23,154✔
1523
    if (only_limit) {
23,118✔
1524
        return find_all(min_limit);
42✔
1525
    }
42✔
1526

11,871✔
1527
    TableView ret(*this, default_limit);
23,076✔
1528
    ret.apply_descriptor_ordering(descriptor);
23,076✔
1529
    return ret;
23,076✔
1530
}
23,076✔
1531

1532
size_t Query::count(const DescriptorOrdering& descriptor) const
1533
{
80,508✔
1534
    if (!m_table)
80,508✔
1535
        return 0;
×
1536
    realm::util::Optional<size_t> min_limit = descriptor.get_min_limit();
80,508✔
1537

39,777✔
1538
    if (bool(min_limit) && *min_limit == 0)
80,508✔
1539
        return 0;
30✔
1540

39,762✔
1541
    size_t limit = size_t(-1);
80,478✔
1542

39,762✔
1543
    if (!descriptor.will_apply_distinct()) {
80,478✔
1544
        if (bool(min_limit)) {
80,460✔
1545
            limit = *min_limit;
180✔
1546
        }
180✔
1547
        return do_count(limit);
80,460✔
1548
    }
80,460✔
1549

9✔
1550
    TableView ret(*this, limit);
18✔
1551
    ret.apply_descriptor_ordering(descriptor);
18✔
1552
    return ret.size();
18✔
1553
}
18✔
1554

1555
// todo, not sure if start, end and limit could be useful for delete.
1556
size_t Query::remove() const
1557
{
2,166✔
1558
    TableView tv = find_all();
2,166✔
1559
    size_t rows = tv.size();
2,166✔
1560
    tv.clear();
2,166✔
1561
    return rows;
2,166✔
1562
}
2,166✔
1563

1564
#if REALM_MULTITHREAD_QUERY
1565
TableView Query::find_all_multi(size_t start, size_t end)
1566
{
1567
    static_cast<void>(start);
1568
    static_cast<void>(end);
1569

1570
    // Initialization
1571
    init();
1572
    ts.next_job = start;
1573
    ts.end_job = end;
1574
    ts.done_job = 0;
1575
    ts.count = 0;
1576
    ts.table = &table;
1577
    ts.node = first[0];
1578

1579
    // Signal all threads to start
1580
    pthread_mutex_unlock(&ts.jobs_mutex);
1581
    pthread_cond_broadcast(&ts.jobs_cond);
1582

1583
    // Wait until all threads have completed
1584
    pthread_mutex_lock(&ts.completed_mutex);
1585
    while (ts.done_job < ts.end_job)
1586
        pthread_cond_wait(&ts.completed_cond, &ts.completed_mutex);
1587
    pthread_mutex_lock(&ts.jobs_mutex);
1588
    pthread_mutex_unlock(&ts.completed_mutex);
1589

1590
    TableView tv(*m_table);
1591

1592
    // Sort search results because user expects ascending order
1593
    sort(ts.chunks.begin(), ts.chunks.end(), &Query::comp);
1594
    for (size_t i = 0; i < ts.chunks.size(); ++i) {
1595
        const size_t from = ts.chunks[i].m_root_node;
1596
        const size_t upto = (i == ts.chunks.size() - 1) ? size_t(-1) : ts.chunks[i + 1].m_root_node;
1597
        size_t first = ts.chunks[i].second;
1598

1599
        while (first < ts.results.size() && ts.results[first] < upto && ts.results[first] >= from) {
1600
            tv.get_ref_column().add(ts.results[first]);
1601
            ++first;
1602
        }
1603
    }
1604

1605
    return move(tv);
1606
}
1607

1608
int Query::set_threads(unsigned int threadcount)
1609
{
1610
#if defined(_WIN32) || defined(__WIN32__) || defined(_WIN64)
1611
    pthread_win32_process_attach_np();
1612
#endif
1613
    pthread_mutex_init(&ts.result_mutex, nullptr);
1614
    pthread_cond_init(&ts.completed_cond, nullptr);
1615
    pthread_mutex_init(&ts.jobs_mutex, nullptr);
1616
    pthread_mutex_init(&ts.completed_mutex, nullptr);
1617
    pthread_cond_init(&ts.jobs_cond, nullptr);
1618

1619
    pthread_mutex_lock(&ts.jobs_mutex);
1620

1621
    for (size_t i = 0; i < m_threadcount; ++i)
1622
        pthread_detach(threads[i]);
1623

1624
    for (size_t i = 0; i < threadcount; ++i) {
1625
        int r = pthread_create(&threads[i], nullptr, query_thread, (void*)&ts);
1626
        if (r != 0)
1627
            REALM_ASSERT(false); // todo
1628
    }
1629
    m_threadcount = threadcount;
1630
    return 0;
1631
}
1632

1633

1634
void* Query::query_thread(void* arg)
1635
{
1636
    static_cast<void>(arg);
1637
    thread_state* ts = static_cast<thread_state*>(arg);
1638

1639
    std::vector<size_t> res;
1640
    std::vector<pair<size_t, size_t>> chunks;
1641

1642
    for (;;) {
1643
        // Main waiting loop that waits for a query to start
1644
        pthread_mutex_lock(&ts->jobs_mutex);
1645
        while (ts->next_job == ts->end_job)
1646
            pthread_cond_wait(&ts->jobs_cond, &ts->jobs_mutex);
1647
        pthread_mutex_unlock(&ts->jobs_mutex);
1648

1649
        for (;;) {
1650
            // Pick a job
1651
            pthread_mutex_lock(&ts->jobs_mutex);
1652
            if (ts->next_job == ts->end_job)
1653
                break;
1654
            const size_t chunk = min(ts->end_job - ts->next_job, thread_chunk_size);
1655
            const size_t mine = ts->next_job;
1656
            ts->next_job += chunk;
1657
            size_t r = mine - 1;
1658
            const size_t end = mine + chunk;
1659

1660
            pthread_mutex_unlock(&ts->jobs_mutex);
1661

1662
            // Execute job
1663
            for (;;) {
1664
                r = ts->node->find_first(r + 1, end);
1665
                if (r == end)
1666
                    break;
1667
                res.push_back(r);
1668
            }
1669

1670
            // Append result in common queue shared by all threads.
1671
            pthread_mutex_lock(&ts->result_mutex);
1672
            ts->done_job += chunk;
1673
            if (res.size() > 0) {
1674
                ts->chunks.push_back(std::pair<size_t, size_t>(mine, ts->results.size()));
1675
                ts->count += res.size();
1676
                for (size_t i = 0; i < res.size(); i++) {
1677
                    ts->results.push_back(res[i]);
1678
                }
1679
                res.clear();
1680
            }
1681
            pthread_mutex_unlock(&ts->result_mutex);
1682

1683
            // Signal main thread that we might have compleeted
1684
            pthread_mutex_lock(&ts->completed_mutex);
1685
            pthread_cond_signal(&ts->completed_cond);
1686
            pthread_mutex_unlock(&ts->completed_mutex);
1687
        }
1688
    }
1689
    return 0;
1690
}
1691

1692
#endif // REALM_MULTITHREADQUERY
1693

1694
std::string Query::validate() const
1695
{
36✔
1696
    if (!m_groups.size())
36✔
1697
        return "";
×
1698

18✔
1699
    if (error_code != "") // errors detected by QueryInterface
36✔
1700
        return error_code;
×
1701

18✔
1702
    if (!root_node())
36✔
1703
        return "Syntax error";
12✔
1704

12✔
1705
    return root_node()->validate(); // errors detected by QueryEngine
24✔
1706
}
24✔
1707

1708
std::string Query::get_description(util::serializer::SerialisationState& state) const
1709
{
233,151✔
1710
    if (m_view) {
233,151✔
1711
        throw SerializationError("Serialization of a query constrained by a view is not currently supported");
1,035✔
1712
    }
1,035✔
1713
    std::string description;
232,116✔
1714
    if (auto root = root_node()) {
232,116✔
1715
        description = root->describe_expression(state);
156,372✔
1716
    }
156,372✔
1717
    else {
75,744✔
1718
        // An empty query returns all results and one way to indicate this
37,641✔
1719
        // is to serialise TRUEPREDICATE which is functionally equivalent
37,641✔
1720
        description = "TRUEPREDICATE";
75,744✔
1721
    }
75,744✔
1722
    if (this->m_ordering) {
232,116✔
1723
        description += " " + m_ordering->get_description(m_table);
504✔
1724
    }
504✔
1725
    return description;
232,116✔
1726
}
232,116✔
1727

1728
Query& Query::set_ordering(util::bind_ptr<DescriptorOrdering> ordering)
1729
{
59,799✔
1730
    m_ordering = std::move(ordering);
59,799✔
1731
    return *this;
59,799✔
1732
}
59,799✔
1733

1734
util::bind_ptr<DescriptorOrdering> Query::get_ordering()
1735
{
492✔
1736
    return std::move(m_ordering);
492✔
1737
}
492✔
1738

1739
std::string Query::get_description() const
1740
{
26,835✔
1741
    util::serializer::SerialisationState state(m_table->get_parent_group());
26,835✔
1742
    return get_description(state);
26,835✔
1743
}
26,835✔
1744

1745
std::string Query::get_description_safe() const noexcept
1746
{
206,100✔
1747
    try {
206,100✔
1748
        util::serializer::SerialisationState state(m_table->get_parent_group());
206,100✔
1749
        return get_description(state);
206,100✔
1750
    }
206,100✔
1751
    catch (const Exception& e) {
1,023✔
1752
        if (auto logger = m_table->get_logger()) {
1,023✔
1753
            logger->log(util::Logger::Level::warn, "Query::get_description() failed: '%1'", e.what());
1,023✔
1754
        }
1,023✔
1755
    }
1,023✔
1756
    return "Unknown Query";
103,026✔
1757
}
206,100✔
1758

1759
void Query::init() const
1760
{
1,919,691✔
1761
    m_table.check();
1,919,691✔
1762
    if (ParentNode* root = root_node()) {
1,919,691✔
1763
        root->init(m_view == nullptr);
1,868,028✔
1764
        std::vector<ParentNode*> vec;
1,868,028✔
1765
        root->gather_children(vec);
1,868,028✔
1766
    }
1,868,028✔
1767
}
1,919,691✔
1768

1769
size_t Query::find_internal(size_t start, size_t end) const
1770
{
×
1771
    if (end == size_t(-1))
×
1772
        end = m_table.unchecked_ptr()->size();
×
1773
    if (start == end)
×
1774
        return not_found;
×
1775

1776
    size_t r;
×
1777
    if (ParentNode* root = root_node())
×
1778
        r = root->find_first(start, end);
×
1779
    else
×
1780
        r = start; // user built an empty query; return any first
×
1781

1782
    if (r == m_table.unchecked_ptr()->size())
×
1783
        return not_found;
×
UNCOV
1784
    else
×
UNCOV
1785
        return r;
×
UNCOV
1786
}
×
1787

1788
void Query::add_node(std::unique_ptr<ParentNode> node)
1789
{
3,004,533✔
1790
    REALM_ASSERT(node);
3,004,533✔
1791
    using State = QueryGroup::State;
3,004,533✔
1792

1,407,681✔
1793
    if (m_table)
3,004,533✔
1794
        node->set_table(m_table);
3,142,017✔
1795

1,407,681✔
1796
    auto& current_group = m_groups.back();
3,004,533✔
1797
    switch (current_group.m_state) {
3,004,533✔
1798
        case QueryGroup::State::OrCondition: {
667,356✔
1799
            REALM_ASSERT_DEBUG(dynamic_cast<OrNode*>(current_group.m_root_node.get()));
667,356✔
1800
            OrNode* or_node = static_cast<OrNode*>(current_group.m_root_node.get());
667,356✔
1801
            or_node->m_conditions.emplace_back(std::move(node));
667,356✔
1802
            current_group.m_state = State::OrConditionChildren;
667,356✔
1803
            break;
667,356✔
UNCOV
1804
        }
×
1805
        case QueryGroup::State::OrConditionChildren: {
66✔
1806
            REALM_ASSERT_DEBUG(dynamic_cast<OrNode*>(current_group.m_root_node.get()));
66✔
1807
            OrNode* or_node = static_cast<OrNode*>(current_group.m_root_node.get());
66✔
1808
            or_node->m_conditions.back()->add_child(std::move(node));
66✔
1809
            break;
66✔
UNCOV
1810
        }
×
1811
        default: {
2,465,430✔
1812
            if (!current_group.m_root_node) {
2,465,430✔
1813
                current_group.m_root_node = std::move(node);
2,442,225✔
1814
            }
2,442,225✔
1815
            else {
23,205✔
1816
                current_group.m_root_node->add_child(std::move(node));
23,205✔
1817
            }
23,205✔
1818
        }
2,465,430✔
1819
    }
3,004,533✔
1820

1,407,681✔
1821
    handle_pending_not();
3,159,057✔
1822
}
3,159,057✔
1823

1824
/* ********************************************************************************************************************
1825
 *
1826
 *  Stuff related to next-generation query syntax
1827
 *
1828
 ********************************************************************************************************************
1829
 */
1830

1831
Query& Query::and_query(const Query& q)
1832
{
21,594✔
1833
    Query copy(q);
21,594✔
1834
    return and_query(std::move(copy));
21,594✔
1835
}
21,594✔
1836

1837
Query& Query::and_query(Query&& q)
1838
{
672,675✔
1839
    if (q.root_node()) {
672,675✔
1840
        add_node(std::move(q.m_groups[0].m_root_node));
672,648✔
1841

336,321✔
1842
        if (q.m_source_collection) {
672,648✔
1843
            REALM_ASSERT(!m_source_collection || (m_source_collection->matches(*q.m_source_collection)));
6✔
1844
            m_source_collection = std::move(q.m_source_collection);
6✔
1845
            m_view = m_source_collection.get();
6✔
1846
        }
6✔
1847
    }
672,648✔
1848

336,336✔
1849
    return *this;
672,675✔
1850
}
672,675✔
1851

1852
Query Query::operator||(const Query& q)
1853
{
438✔
1854
    Query q2(m_table);
438✔
1855
    q2.and_query(*this);
438✔
1856
    q2.Or();
438✔
1857
    q2.and_query(q);
438✔
1858

219✔
1859
    return q2;
438✔
1860
}
438✔
1861

1862

1863
Query Query::operator&&(const Query& q)
1864
{
558✔
1865
    if (!root_node())
558✔
1866
        return q;
36✔
1867

261✔
1868
    if (!q.root_node())
522✔
1869
        return *this;
36✔
1870

243✔
1871
    Query q2(m_table);
486✔
1872
    q2.and_query(*this);
486✔
1873
    q2.and_query(q);
486✔
1874

243✔
1875
    return q2;
486✔
1876
}
486✔
1877

1878

1879
Query Query::operator!()
1880
{
48✔
1881
    if (!root_node())
48✔
1882
        throw Exception(ErrorCodes::InvalidQuery, "negation of empty query is not supported");
6✔
1883
    Query q(m_table);
42✔
1884
    q.Not();
42✔
1885
    q.and_query(*this);
42✔
1886
    return q;
42✔
1887
}
42✔
1888

1889
void Query::get_outside_versions(TableVersions& versions) const
1890
{
2,029,338✔
1891
    if (m_table) {
2,036,643✔
1892
        if (m_table_keys.empty()) {
2,036,643✔
1893
            // Store primary table info
838,182✔
1894
            m_table_keys.push_back(m_table.unchecked_ptr()->get_key());
1,707,267✔
1895

838,182✔
1896
            if (ParentNode* root = root_node())
1,707,267✔
1897
                root->get_link_dependencies(m_table_keys);
1,579,173✔
1898
        }
1,707,267✔
1899
        versions.emplace_back(m_table.unchecked_ptr()->get_key(), m_table.unchecked_ptr()->get_content_version());
2,036,643✔
1900

955,848✔
1901
        if (Group* g = m_table.unchecked_ptr()->get_parent_group()) {
2,106,138✔
1902
            // update table versions for linked tables - first entry is primary table - skip it
1,025,343✔
1903
            auto end = m_table_keys.end();
2,090,208✔
1904
            auto it = m_table_keys.begin() + 1;
2,090,208✔
1905
            while (it != end) {
2,093,058✔
1906
                versions.emplace_back(*it, g->get_table(*it)->get_content_version());
2,850✔
1907
                ++it;
2,850✔
1908
            }
2,850✔
1909
        }
2,090,208✔
1910
        if (m_view) {
2,036,643✔
1911
            m_view->get_dependencies(versions);
5,118✔
1912
        }
5,118✔
1913
    }
2,036,643✔
1914
}
2,029,338✔
1915

1916
TableVersions Query::sync_view_if_needed() const
1917
{
153,324✔
1918
    if (m_view) {
153,324✔
1919
        m_view->sync_if_needed();
993✔
1920
    }
993✔
1921
    TableVersions ret;
153,324✔
1922
    get_outside_versions(ret);
153,324✔
1923
    return ret;
153,324✔
1924
}
153,324✔
1925

1926
QueryGroup::QueryGroup(const QueryGroup& other)
1927
    : m_root_node(other.m_root_node ? other.m_root_node->clone() : nullptr)
1928
    , m_pending_not(other.m_pending_not)
1929
    , m_state(other.m_state)
1930
{
2,433,153✔
1931
}
2,433,153✔
1932

1933
QueryGroup& QueryGroup::operator=(const QueryGroup& other)
1934
{
25,506✔
1935
    if (this != &other) {
25,506✔
1936
        m_root_node = other.m_root_node ? other.m_root_node->clone() : nullptr;
25,500✔
1937
        m_pending_not = other.m_pending_not;
25,506✔
1938
    }
25,506✔
1939
    return *this;
25,506✔
1940
}
25,506✔
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