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

realm / realm-core / github_pull_request_281750

30 Oct 2023 03:37PM UTC coverage: 90.528% (-1.0%) from 91.571%
github_pull_request_281750

Pull #6073

Evergreen

jedelbo
Log free space and history sizes when opening file
Pull Request #6073: Merge next-major

95488 of 175952 branches covered (0.0%)

8973 of 12277 new or added lines in 149 files covered. (73.09%)

622 existing lines in 51 files now uncovered.

233503 of 257934 relevant lines covered (90.53%)

6533720.56 hits per line

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

92.71
/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,072✔
37
    create();
171,072✔
38
}
171,072✔
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,366,754✔
65
    create();
2,366,754✔
66
}
2,366,754✔
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,476,746✔
79
    if (m_table && m_table->is_asymmetric()) {
2,476,746✔
80
        throw IllegalOperation{"Query on ephemeral objects not allowed"};
12✔
81
    }
12✔
82
    m_groups.emplace_back();
2,476,734✔
83
}
2,476,734✔
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,354,703✔
91
    if (source.m_owned_source_table_view) {
2,354,703✔
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,354,685✔
96
        // FIXME: The lifetime of `m_source_table_view` may be tied to that of `source`, which can easily
1,107,342✔
97
        // turn `m_source_table_view` into a dangling reference.
1,107,342✔
98
        m_source_table_view = source.m_source_table_view;
2,354,685✔
99
        m_source_collection = source.m_source_collection ? source.m_source_collection->clone_obj_list() : nullptr;
2,354,088✔
100
    }
2,354,685✔
101
    if (m_source_table_view) {
2,354,703✔
102
        m_view = m_source_table_view;
240✔
103
    }
240✔
104
    else if (m_source_collection) {
2,354,463✔
105
        m_view = m_source_collection.get();
1,188✔
106
    }
1,188✔
107
}
2,354,703✔
108

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

12,339✔
115
        if (source.m_owned_source_table_view) {
25,104✔
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,098✔
122
            // FIXME: The lifetime of `m_source_table_view` may be tied to that of `source`, which can easily
12,336✔
123
            // turn `m_source_table_view` into a dangling reference.
12,336✔
124
            m_source_table_view = source.m_source_table_view;
25,098✔
125
            m_owned_source_table_view = nullptr;
25,098✔
126

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

140
Query::Query(Query&&) = default;
306,282✔
141
Query& Query::operator=(Query&&) = default;
27,345✔
142

143
Query::~Query() noexcept = default;
5,112,432✔
144

145
Query::Query(const Query* source, Transaction* tr, PayloadPolicy policy)
146
{
117,126✔
147
    if (source->m_source_table_view) {
117,126✔
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 {
117,102✔
153
        // nothing?
58,482✔
154
    }
117,102✔
155
    if (source->m_source_collection) {
117,126✔
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;
117,126✔
161
    if (source->m_table)
117,126✔
162
        set_table(tr->import_copy_of(source->m_table));
117,126✔
163
    // otherwise: empty query.
58,494✔
164
}
117,126✔
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,722✔
171

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

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

90,873✔
181
    if (tr->is_asymmetric()) {
181,887✔
182
        throw IllegalOperation{"Query on ephemeral objects not allowed"};
×
183
    }
×
184

90,873✔
185
    m_table = tr;
181,887✔
186
    if (m_table) {
181,887✔
187
        ParentNode* root = root_node();
181,881✔
188
        if (root)
181,881✔
189
            root->set_table(m_table);
55,638✔
190
    }
181,881✔
191
}
181,887✔
192

193

194
void Query::add_expression_node(std::unique_ptr<Expression> expression)
195
{
65,646✔
196
    add_node(std::unique_ptr<ParentNode>(new ExpressionNode(std::move(expression))));
65,646✔
197
}
65,646✔
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,198✔
273
        return std::unique_ptr<ParentNode>{new Node(std::move(value), col_key)};
672,198✔
274
    }
672,198✔
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,446,558✔
300
        return std::unique_ptr<ParentNode>{new IntegerNode<ArrayInteger, Cond>(std::move(value), col_key)};
1,446,558✔
301
    }
1,446,558✔
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,123✔
314
        return std::unique_ptr<ParentNode>{new StringNode<Cond>(std::move(value), col_key)};
54,123✔
315
    }
54,123✔
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,398✔
334
        return std::unique_ptr<ParentNode>{new MixedNode<Cond>(Mixed(value), col_key)};
1,398✔
335
    }
1,398✔
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,197,632✔
341
    table.check_column(column_key);
2,197,632✔
342
    DataType type = DataType(column_key.get_type());
2,197,632✔
343
    switch (type) {
2,197,632✔
344
        case type_Int: {
1,553,646!
345
            if (column_key.get_attrs().test(col_attr_Nullable)) {
1,553,646!
346
                return MakeConditionNode<IntegerNode<ArrayIntNull, Cond>>::make(column_key, value);
16,560✔
347
            }
16,560✔
348
            else {
1,537,086✔
349
                return MakeConditionNode<IntegerNode<ArrayInteger, Cond>>::make(column_key, value);
1,537,086✔
350
            }
1,537,086✔
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,567!
362
            return MakeConditionNode<StringNode<Cond>>::make(column_key, value);
54,567✔
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,758!
374
            return MakeConditionNode<ObjectIdNode<Cond>>::make(column_key, value);
643,758✔
375
        }
×
376
        case type_Mixed: {
1,398!
377
            return MakeConditionNode<MixedNode<Cond>>::make(column_key, value);
1,398✔
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,234,142✔
435
    auto node = make_condition_node<TConditionFunction>(*m_table, column_key, value);
2,234,142✔
436
    add_node(std::move(node));
2,234,142✔
437
    return *this;
2,234,142✔
438
}
2,234,142✔
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,340,298✔
503
    return equal(column_key, static_cast<int64_t>(value));
1,340,298✔
504
}
1,340,298✔
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
{
7,392✔
511
    return greater(column_key, static_cast<int64_t>(value));
7,392✔
512
}
7,392✔
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
{
174✔
519
    return less_equal(column_key, static_cast<int64_t>(value));
174✔
520
}
174✔
521
Query& Query::less(ColKey column_key, int value)
522
{
1,425✔
523
    return less(column_key, static_cast<int64_t>(value));
1,425✔
524
}
1,425✔
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,478,067✔
557
    add_condition<Equal>(column_key, value);
1,478,067✔
558
    return *this;
1,478,067✔
559
}
1,478,067✔
560
Query& Query::not_equal(ColKey column_key, int64_t value)
561
{
29,712✔
562
    add_condition<NotEqual>(column_key, value);
29,712✔
563
    return *this;
29,712✔
564
}
29,712✔
565
Query& Query::greater(ColKey column_key, int64_t value)
566
{
28,713✔
567
    add_condition<Greater>(column_key, value);
28,713✔
568
    return *this;
28,713✔
569
}
28,713✔
570
Query& Query::greater_equal(ColKey column_key, int64_t value)
571
{
15,243✔
572
    if (value > LLONG_MIN) {
15,243✔
573
        add_condition<Greater>(column_key, value - 1);
15,237✔
574
    }
15,237✔
575
    // field >= LLONG_MIN has no effect
7,881✔
576
    return *this;
15,243✔
577
}
15,243✔
578
Query& Query::less_equal(ColKey column_key, int64_t value)
579
{
7,452✔
580
    if (value < LLONG_MAX) {
7,452✔
581
        add_condition<Less>(column_key, value + 1);
7,446✔
582
    }
7,446✔
583
    // field <= LLONG_MAX has no effect
3,726✔
584
    return *this;
7,452✔
585
}
7,452✔
586
Query& Query::less(ColKey column_key, int64_t value)
587
{
10,695✔
588
    add_condition<Less>(column_key, value);
10,695✔
589
    return *this;
10,695✔
590
}
10,695✔
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,116✔
713
    return add_condition<Equal>(column_key, value);
643,116✔
714
}
643,116✔
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
{
1,164✔
799
    if (case_sensitive)
1,164✔
800
        return add_condition<Equal>(column_key, value);
1,080✔
801
    else
84✔
802
        return add_condition<EqualIns>(column_key, value);
84✔
803
}
1,164✔
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
{
42,915✔
894
    if (case_sensitive)
42,915✔
895
        add_condition<Equal>(column_key, value);
41,943✔
896
    else
972✔
897
        add_condition<EqualIns>(column_key, value);
972✔
898
    return *this;
42,915✔
899
}
42,915✔
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,277,730✔
971
    if (has_conditions())
1,277,730✔
972
        return obj && root_node()->match(obj);
1,265,232✔
973

6,186✔
974
    // Query has no conditions, so all rows match, also the user given argument
6,186✔
975
    return true;
12,525✔
976
}
12,525✔
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,728,447✔
1045
    auto score_compare = [](const ParentNode* a, const ParentNode* b) {
3,567,555✔
1046
        return a->cost() < b->cost();
94,119✔
1047
    };
94,119✔
1048
    size_t best = std::distance(pn->m_children.begin(),
7,728,447✔
1049
                                std::min_element(pn->m_children.begin(), pn->m_children.end(), score_compare));
7,728,447✔
1050
    return best;
7,728,447✔
1051
}
7,728,447✔
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,882,676✔
1063
    // Number of matches to find in best condition loop before breaking out to probe other conditions. Too low value
2,560,635✔
1064
    // gives too many constant time overheads everywhere in the query engine. Too high value makes it adapt less
2,560,635✔
1065
    // rapidly to changes in match frequencies.
2,560,635✔
1066
    constexpr size_t findlocals = 64;
5,882,676✔
1067

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

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

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

2,544,126✔
1086
        // Make remaining conditions compute their m_dD (statistics)
2,544,126✔
1087
        for (size_t c = 0; c < pn->m_children.size() && start < end; c++) {
6,215,421✔
1088
            if (c == best)
17,583✔
1089
                continue;
8,256✔
1090

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

4,617✔
1094
                // Limit to bestdist in order not to skip too large parts of index nodes
4,617✔
1095
                size_t maxD = pn->m_children[c]->m_dT == 0.0 ? end - start : bestdist;
9,327✔
1096
                size_t td = pn->m_children[c]->m_dT == 0.0 ? end : (start + maxD > end ? end : start + maxD);
9,327✔
1097
                start = pn->m_children[c]->aggregate_local(st, start, td, probe_matches, source_column);
9,327✔
1098
            }
9,327✔
1099
        }
9,327✔
1100
    }
5,864,043✔
1101
}
5,882,676✔
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
{
17,295✔
1128
    m_groups.emplace_back();
17,295✔
1129
    return *this;
17,295✔
1130
}
17,295✔
1131
Query& Query::end_group()
1132
{
17,295✔
1133
    if (m_groups.size() < 2) {
17,295✔
1134
        error_code = "Unbalanced group";
6✔
1135
        return *this;
6✔
1136
    }
6✔
1137

8,904✔
1138
    auto end_root_node = std::move(m_groups.back().m_root_node);
17,289✔
1139
    m_groups.pop_back();
17,289✔
1140

8,904✔
1141
    if (end_root_node) {
17,289✔
1142
        add_node(std::move(end_root_node));
17,229✔
1143
    }
17,229✔
1144

8,904✔
1145
    handle_pending_not();
17,289✔
1146
    return *this;
17,289✔
1147
}
17,289✔
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,118,473✔
1164
    auto& current_group = m_groups.back();
3,118,473✔
1165
    if (m_groups.size() > 1 && current_group.m_pending_not) {
3,118,473✔
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,118,473✔
1176

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

332,427✔
1186
    return *this;
664,320✔
1187
}
664,320✔
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::LogCategory::query, util::Logger::Level::debug)) {
26,130✔
NEW
1202
        logger->log(util::LogCategory::query, util::Logger::Level::debug, "Query find first: '%1'",
×
NEW
1203
                    get_description_safe());
×
UNCOV
1204
        t1 = std::chrono::steady_clock::now();
×
UNCOV
1205
        do_log = true;
×
UNCOV
1206
    }
×
1207

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

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

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

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

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

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

713,802✔
1275
    if (st.limit() == 0) {
1,512,822✔
1276
        if (logger) {
36✔
NEW
1277
            logger->log(util::LogCategory::query, util::Logger::Level::debug,
×
NEW
1278
                        "Query find all: limit = 0 -> result: 0");
×
UNCOV
1279
        }
×
1280
        return;
36✔
1281
    }
36✔
1282

713,784✔
1283
    if (logger && logger->would_log(util::LogCategory::query, util::Logger::Level::debug)) {
1,512,786✔
1284
        logger->log(util::LogCategory::query, util::Logger::Level::debug, "Query find all: '%1', limit = %2",
6✔
1285
                    get_description_safe(), int64_t(st.limit()));
6✔
1286
        t1 = std::chrono::steady_clock::now();
6✔
1287
        do_log = true;
6✔
1288
    }
6✔
1289

713,784✔
1290
    init();
1,512,786✔
1291

713,784✔
1292
    bool has_cond = has_conditions();
1,512,786✔
1293

713,784✔
1294
    if (m_view) {
1,512,786✔
1295
        size_t sz = m_view->size();
1,101✔
1296
        for (size_t t = 0; t < sz; t++) {
224,079✔
1297
            const Obj obj = m_view->get_object(t);
222,978✔
1298
            if (eval_object(obj)) {
222,978✔
1299
                st.m_key_offset = obj.get_key().value;
74,706✔
1300
                if (!st.match(0, Mixed()))
74,706✔
1301
                    break;
×
1302
            }
74,706✔
1303
        }
222,978✔
1304
    }
1,101✔
1305
    else {
1,511,685✔
1306
        if (!has_cond) {
1,511,685✔
1307
            auto f = [&st](const Cluster* cluster) {
152,310✔
1308
                size_t sz = cluster->node_size();
152,310✔
1309
                st.m_key_offset = cluster->get_offset();
152,310✔
1310
                st.m_key_values = cluster->get_key_array();
152,310✔
1311
                for (size_t i = 0; i < sz; i++) {
27,916,263✔
1312
                    if (!st.match(i, Mixed()))
27,764,049✔
1313
                        return IteratorControl::Stop;
96✔
1314
                }
27,764,049✔
1315
                return IteratorControl::AdvanceToNext;
152,262✔
1316
            };
152,310✔
1317

33,381✔
1318
            m_table->traverse_clusters(f);
67,224✔
1319
        }
67,224✔
1320
        else {
1,444,461✔
1321
            auto pn = root_node();
1,444,461✔
1322
            auto best = find_best_node(pn);
1,444,461✔
1323
            auto node = pn->m_children[best];
1,444,461✔
1324
            if (node->has_search_index()) {
1,444,461✔
1325
                auto keys = node->index_based_keys();
8,730✔
1326
                REALM_ASSERT(keys);
8,730✔
1327

4,365✔
1328
                // The node having the search index can be removed from the query as we know that
4,365✔
1329
                // all the objects will match this condition
4,365✔
1330
                pn->m_children[best] = pn->m_children.back();
8,730✔
1331
                pn->m_children.pop_back();
8,730✔
1332

4,365✔
1333
                const size_t num_keys = keys->size();
8,730✔
1334
                for (size_t i = 0; i < num_keys; ++i) {
242,391✔
1335
                    ObjKey key = keys->get(i);
233,673✔
1336
                    st.m_key_offset = key.value;
233,673✔
1337
                    if (pn->m_children.empty()) {
233,673✔
1338
                        // No more conditions - just add key
98,295✔
1339
                        if (!st.match(0, Mixed()))
196,026✔
1340
                            break;
6✔
1341
                    }
37,647✔
1342
                    else {
37,647✔
1343
                        auto obj = m_table->get_object(key);
37,647✔
1344
                        if (eval_object(obj)) {
37,647✔
1345
                            if (!st.match(0, Mixed()))
18,876✔
1346
                                break;
6✔
1347
                        }
18,876✔
1348
                    }
37,647✔
1349
                }
233,673✔
1350
            }
8,730✔
1351
            else {
1,435,731✔
1352
                // no index on best node (and likely no index at all), descend B+-tree
675,489✔
1353
                node = pn;
1,435,731✔
1354

675,489✔
1355
                auto f = [&node, &st, this](const Cluster* cluster) {
4,621,374✔
1356
                    size_t e = cluster->node_size();
4,621,374✔
1357
                    node->set_cluster(cluster);
4,621,374✔
1358
                    st.m_key_offset = cluster->get_offset();
4,621,374✔
1359
                    st.m_key_values = cluster->get_key_array();
4,621,374✔
1360
                    aggregate_internal(node, &st, 0, e, nullptr);
4,621,374✔
1361
                    // Stop if limit is reached
1,719,837✔
1362
                    return st.match_count() == st.limit() ? IteratorControl::Stop : IteratorControl::AdvanceToNext;
4,621,191✔
1363
                };
4,621,374✔
1364

675,489✔
1365
                m_table->traverse_clusters(f);
1,435,731✔
1366
            }
1,435,731✔
1367
        }
1,444,461✔
1368
    }
1,511,685✔
1369

713,784✔
1370

713,784✔
1371
    if (do_log) {
1,512,786✔
1372
        auto t2 = std::chrono::steady_clock::now();
6✔
1373
        logger->log(util::LogCategory::query, util::Logger::Level::debug, "Query found: %1, Duration: %2 us",
6✔
1374
                    st.match_count(), std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count());
6✔
1375
    }
6✔
1376
}
1,512,786✔
1377

1378
TableView Query::find_all(size_t limit) const
1379
{
1,384,932✔
1380
    TableView ret(*this, limit);
1,384,932✔
1381
    if (m_ordering) {
1,384,932✔
1382
        // apply_descriptor_ordering will call do_sync
207✔
1383
        ret.apply_descriptor_ordering(*m_ordering);
414✔
1384
    }
414✔
1385
    else {
1,384,518✔
1386
        ret.do_sync();
1,384,518✔
1387
    }
1,384,518✔
1388
    return ret;
1,384,932✔
1389
}
1,384,932✔
1390

1391

1392
size_t Query::do_count(size_t limit) const
1393
{
320,451✔
1394
    auto logger = m_table->get_logger();
320,451✔
1395
    std::chrono::steady_clock::time_point t1;
320,451✔
1396
    bool do_log = false;
320,451✔
1397

159,720✔
1398
    if (limit == 0) {
320,451✔
1399
        if (logger) {
×
NEW
1400
            logger->log(util::LogCategory::query, util::Logger::Level::debug, "Query count: limit = 0 -> result: 0");
×
1401
        }
×
1402
        return 0;
×
1403
    }
×
1404

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

152,388✔
1421
    if (logger && logger->would_log(util::LogCategory::query, util::Logger::Level::debug)) {
305,787✔
1422
        logger->log(util::LogCategory::query, util::Logger::Level::debug, "Query count: '%1', limit = %2",
6✔
1423
                    get_description_safe(), int64_t(limit));
6✔
1424
        t1 = std::chrono::steady_clock::now();
6✔
1425
        do_log = true;
6✔
1426
    }
6✔
1427
    size_t cnt = 0;
305,787✔
1428

152,388✔
1429
    init();
305,787✔
1430

152,388✔
1431
    if (m_view) {
305,787✔
1432
        m_view->for_each([&](const Obj& obj) {
969,372✔
1433
            if (eval_object(obj)) {
969,372✔
1434
                cnt++;
483,696✔
1435
            }
483,696✔
1436
            return IteratorControl::AdvanceToNext;
969,372✔
1437
        });
969,372✔
1438
    }
420✔
1439
    else {
305,367✔
1440
        auto pn = root_node();
305,367✔
1441
        auto best = find_best_node(pn);
305,367✔
1442
        auto node = pn->m_children[best];
305,367✔
1443
        if (node->has_search_index()) {
305,367✔
1444
            auto keys = node->index_based_keys();
2,088✔
1445
            REALM_ASSERT(keys);
2,088✔
1446
            if (pn->m_children.size() > 1) {
2,088✔
1447
                // The node having the search index can be removed from the query as we know that
42✔
1448
                // all the objects will match this condition
42✔
1449
                pn->m_children[best] = pn->m_children.back();
84✔
1450
                pn->m_children.pop_back();
84✔
1451
                const size_t num_keys = keys->size();
84✔
1452
                for (size_t i = 0; i < num_keys; ++i) {
28,383✔
1453
                    auto obj = m_table->get_object(keys->get(i));
28,299✔
1454
                    if (eval_object(obj)) {
28,299✔
1455
                        ++cnt;
13,914✔
1456
                        if (cnt == limit)
13,914✔
1457
                            break;
×
1458
                    }
13,914✔
1459
                }
28,299✔
1460
            }
84✔
1461
            else {
2,004✔
1462
                // The node having the search index is the only node
1,002✔
1463
                auto sz = keys->size();
2,004✔
1464
                cnt = std::min(limit, sz);
2,004✔
1465
            }
2,004✔
1466
        }
2,088✔
1467
        else {
303,279✔
1468
            // no index, descend down the B+-tree instead
151,134✔
1469
            node = pn;
303,279✔
1470
            QueryStateCount st(limit);
303,279✔
1471

151,134✔
1472
            auto f = [&node, &st, this](const Cluster* cluster) {
541,650✔
1473
                size_t e = cluster->node_size();
541,650✔
1474
                node->set_cluster(cluster);
541,650✔
1475
                st.m_key_offset = cluster->get_offset();
541,650✔
1476
                st.m_key_values = cluster->get_key_array();
541,650✔
1477
                aggregate_internal(node, &st, 0, e, nullptr);
541,650✔
1478
                // Stop if limit or end is reached
269,637✔
1479
                return st.match_count() == st.limit() ? IteratorControl::Stop : IteratorControl::AdvanceToNext;
541,635✔
1480
            };
541,650✔
1481

151,134✔
1482
            m_table->traverse_clusters(f);
303,279✔
1483

151,134✔
1484
            cnt = st.get_count();
303,279✔
1485
        }
303,279✔
1486
    }
305,367✔
1487

152,388✔
1488
    if (do_log) {
305,787✔
1489
        auto t2 = std::chrono::steady_clock::now();
6✔
1490
        logger->log(util::LogCategory::query, util::Logger::Level::debug, "Query matches: %1, Duration: %2 us", cnt,
6✔
1491
                    std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count());
6✔
1492
    }
6✔
1493

152,388✔
1494
    return cnt;
305,787✔
1495
}
305,787✔
1496

1497
size_t Query::count() const
1498
{
241,014✔
1499
    if (!m_table)
241,014✔
1500
        return 0;
×
1501
    return do_count();
241,014✔
1502
}
241,014✔
1503

1504
TableView Query::find_all(const DescriptorOrdering& descriptor) const
1505
{
50,898✔
1506
    if (descriptor.is_empty()) {
50,898✔
1507
        return find_all();
30,810✔
1508
    }
30,810✔
1509

10,314✔
1510
    const size_t default_limit = size_t(-1);
20,088✔
1511

10,314✔
1512
    bool only_limit = true;
20,088✔
1513
    size_t min_limit = size_t(-1);
20,088✔
1514
    for (size_t i = 0; i < descriptor.size(); ++i) {
20,166✔
1515
        if (descriptor.get_type(i) != DescriptorType::Limit) {
20,124✔
1516
            only_limit = false;
20,046✔
1517
            break;
20,046✔
1518
        }
20,046✔
1519
        else {
78✔
1520
            REALM_ASSERT(dynamic_cast<const LimitDescriptor*>(descriptor[i]));
78✔
1521
            const LimitDescriptor* limit = static_cast<const LimitDescriptor*>(descriptor[i]);
78✔
1522
            min_limit = std::min(min_limit, limit->get_limit());
78✔
1523
        }
78✔
1524
    }
20,124✔
1525
    if (only_limit) {
20,088✔
1526
        return find_all(min_limit);
42✔
1527
    }
42✔
1528

10,293✔
1529
    TableView ret(*this, default_limit);
20,046✔
1530
    ret.apply_descriptor_ordering(descriptor);
20,046✔
1531
    return ret;
20,046✔
1532
}
20,046✔
1533

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

39,282✔
1540
    if (bool(min_limit) && *min_limit == 0)
79,518✔
1541
        return 0;
30✔
1542

39,267✔
1543
    size_t limit = size_t(-1);
79,488✔
1544

39,267✔
1545
    if (!descriptor.will_apply_distinct() && !descriptor.will_apply_filter()) {
79,488✔
1546
        if (bool(min_limit)) {
79,452✔
1547
            limit = *min_limit;
180✔
1548
        }
180✔
1549
        return do_count(limit);
79,452✔
1550
    }
79,452✔
1551

18✔
1552
    TableView ret(*this, limit);
36✔
1553
    ret.apply_descriptor_ordering(descriptor);
36✔
1554
    return ret.size();
36✔
1555
}
36✔
1556

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

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

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

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

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

1592
    TableView tv(*m_table);
1593

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

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

1607
    return move(tv);
1608
}
1609

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

1621
    pthread_mutex_lock(&ts.jobs_mutex);
1622

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

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

1635

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

1641
    std::vector<size_t> res;
1642
    std::vector<pair<size_t, size_t>> chunks;
1643

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

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

1662
            pthread_mutex_unlock(&ts->jobs_mutex);
1663

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

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

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

1694
#endif // REALM_MULTITHREADQUERY
1695

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

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

18✔
1704
    if (!root_node())
36✔
1705
        return "Syntax error";
12✔
1706

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

1710
std::string Query::get_description(util::serializer::SerialisationState& state) const
1711
{
27,213✔
1712
    std::string description;
27,213✔
1713
    if (auto root = root_node()) {
27,213✔
1714
        description = root->describe_expression(state);
26,706✔
1715
    }
26,706✔
1716
    if (m_view) {
27,213✔
1717
        description += util::format(" VIEW { %1 element(s) }", m_view->size());
18✔
1718
    }
18✔
1719
    if (description.length() == 0) {
27,213✔
1720
        // An empty query returns all results and one way to indicate this
252✔
1721
        // is to serialise TRUEPREDICATE which is functionally equivalent
252✔
1722
        description = "TRUEPREDICATE";
507✔
1723
    }
507✔
1724
    if (m_ordering) {
27,213✔
1725
        description += " " + m_ordering->get_description(m_table);
480✔
1726
    }
480✔
1727
    return description;
27,213✔
1728
}
27,213✔
1729

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

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

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

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

1761
void Query::init() const
1762
{
1,861,098✔
1763
    m_table.check();
1,861,098✔
1764
    if (ParentNode* root = root_node()) {
1,878,456✔
1765
        root->init(m_view == nullptr);
1,839,375✔
1766
        std::vector<ParentNode*> vec;
1,839,375✔
1767
        root->gather_children(vec);
1,839,375✔
1768
    }
1,839,375✔
1769
}
1,861,098✔
1770

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

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

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

1790
void Query::add_node(std::unique_ptr<ParentNode> node)
1791
{
3,028,761✔
1792
    REALM_ASSERT(node);
3,028,761✔
1793
    using State = QueryGroup::State;
3,028,761✔
1794

1,449,225✔
1795
    if (m_table)
3,028,761✔
1796
        node->set_table(m_table);
3,090,162✔
1797

1,449,225✔
1798
    auto& current_group = m_groups.back();
3,028,761✔
1799
    switch (current_group.m_state) {
3,028,761✔
1800
        case QueryGroup::State::OrCondition: {
664,296✔
1801
            REALM_ASSERT_DEBUG(dynamic_cast<OrNode*>(current_group.m_root_node.get()));
664,296✔
1802
            OrNode* or_node = static_cast<OrNode*>(current_group.m_root_node.get());
664,296✔
1803
            or_node->m_conditions.emplace_back(std::move(node));
664,296✔
1804
            current_group.m_state = State::OrConditionChildren;
664,296✔
1805
            break;
664,296✔
1806
        }
×
1807
        case QueryGroup::State::OrConditionChildren: {
66✔
1808
            REALM_ASSERT_DEBUG(dynamic_cast<OrNode*>(current_group.m_root_node.get()));
66✔
1809
            OrNode* or_node = static_cast<OrNode*>(current_group.m_root_node.get());
66✔
1810
            or_node->m_conditions.back()->add_child(std::move(node));
66✔
1811
            break;
66✔
1812
        }
×
1813
        default: {
2,303,976✔
1814
            if (!current_group.m_root_node) {
2,416,665✔
1815
                current_group.m_root_node = std::move(node);
2,397,861✔
1816
            }
2,397,861✔
1817
            else {
2,147,502,451✔
1818
                current_group.m_root_node->add_child(std::move(node));
2,147,502,451✔
1819
            }
2,147,502,451✔
1820
        }
2,303,976✔
1821
    }
3,028,761✔
1822

1,449,225✔
1823
    handle_pending_not();
3,103,944✔
1824
}
3,103,944✔
1825

1826
/* ********************************************************************************************************************
1827
 *
1828
 *  Stuff related to next-generation query syntax
1829
 *
1830
 ********************************************************************************************************************
1831
 */
1832

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

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

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

336,336✔
1851
    return *this;
672,675✔
1852
}
672,675✔
1853

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

219✔
1861
    return q2;
438✔
1862
}
438✔
1863

1864

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

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

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

243✔
1877
    return q2;
486✔
1878
}
486✔
1879

1880

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

1891
void Query::get_outside_versions(TableVersions& versions) const
1892
{
2,017,131✔
1893
    if (m_table) {
2,061,138✔
1894
        if (m_table_keys.empty()) {
2,061,138✔
1895
            // Store primary table info
857,505✔
1896
            m_table_keys.push_back(m_table.unchecked_ptr()->get_key());
1,717,059✔
1897

857,505✔
1898
            if (ParentNode* root = root_node())
1,717,059✔
1899
                root->get_link_dependencies(m_table_keys);
1,589,274✔
1900
        }
1,717,059✔
1901
        versions.emplace_back(m_table.unchecked_ptr()->get_key(), m_table.unchecked_ptr()->get_content_version());
2,061,138✔
1902

995,145✔
1903
        if (Group* g = m_table.unchecked_ptr()->get_parent_group()) {
2,061,138✔
1904
            // update table versions for linked tables - first entry is primary table - skip it
983,931✔
1905
            auto end = m_table_keys.end();
2,035,893✔
1906
            auto it = m_table_keys.begin() + 1;
2,035,893✔
1907
            while (it != end) {
2,038,581✔
1908
                versions.emplace_back(*it, g->get_table(*it)->get_content_version());
2,688✔
1909
                ++it;
2,688✔
1910
            }
2,688✔
1911
        }
2,035,893✔
1912
        if (m_view) {
2,061,138✔
1913
            m_view->get_dependencies(versions);
4,578✔
1914
        }
4,578✔
1915
    }
2,061,138✔
1916
}
2,017,131✔
1917

1918
TableVersions Query::sync_view_if_needed() const
1919
{
134,712✔
1920
    if (m_view) {
134,712✔
1921
        m_view->sync_if_needed();
813✔
1922
    }
813✔
1923
    TableVersions ret;
134,712✔
1924
    get_outside_versions(ret);
134,712✔
1925
    return ret;
134,712✔
1926
}
134,712✔
1927

1928
QueryGroup::QueryGroup(const QueryGroup& other)
1929
    : m_root_node(other.m_root_node ? other.m_root_node->clone() : nullptr)
1930
    , m_pending_not(other.m_pending_not)
1931
    , m_state(other.m_state)
1932
{
2,463,078✔
1933
}
2,463,078✔
1934

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