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

realm / realm-core / 2308

10 May 2024 05:50PM UTC coverage: 90.8% (-0.05%) from 90.846%
2308

push

Evergreen

danieltabacaru
Add back ability to format Objective-C code

102056 of 181070 branches covered (56.36%)

214529 of 236265 relevant lines covered (90.8%)

5512753.1 hits per line

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

94.11
/src/realm/query_engine.hpp
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
/*
20
A query consists of node objects, one for each query condition. Each node contains pointers to all other nodes:
21

22
node1        node2         node3
23
------       -----         -----
24
node2*       node1*        node1*
25
node3*       node3*        node2*
26

27
The construction of all this takes part in query.cpp. Each node has two important functions:
28

29
    aggregate(start, end)
30
    aggregate_local(start, end)
31

32
The aggregate() function executes the aggregate of a query. You can call the method on any of the nodes
33
(except children nodes of OrNode and SubtableNode) - it has the same behaviour. The function contains
34
scheduling that calls aggregate_local(start, end) on different nodes with different start/end ranges,
35
depending on what it finds is most optimal.
36

37
The aggregate_local() function contains a tight loop that tests the condition of its own node, and upon match
38
it tests all other conditions at that index to report a full match or not. It will remain in the tight loop
39
after a full match.
40

41
So a call stack with 2 and 9 being local matches of a node could look like this:
42

43
aggregate(0, 10)
44
    node1->aggregate_local(0, 3)
45
        node2->find_first_local(2, 3)
46
        node3->find_first_local(2, 3)
47
    node3->aggregate_local(3, 10)
48
        node1->find_first_local(4, 5)
49
        node2->find_first_local(4, 5)
50
        node1->find_first_local(7, 8)
51
        node2->find_first_local(7, 8)
52

53
find_first_local(n, n + 1) is a function that can be used to test a single row of another condition. Note that
54
this is very simplified. There are other statistical arguments to the methods, and also, find_first_local() can be
55
called from a callback function called by an integer Array.
56

57

58
Template arguments in methods:
59
----------------------------------------------------------------------------------------------------
60

61
TConditionFunction: Each node has a condition from query_conditions.c such as Equal, GreaterEqual, etc
62

63
TConditionValue:    Type of values in condition column. That is, int64_t, float, int, bool, etc
64
*/
65

66
#ifndef REALM_QUERY_ENGINE_HPP
67
#define REALM_QUERY_ENGINE_HPP
68

69
#include <algorithm>
70
#include <array>
71
#include <functional>
72
#include <sstream>
73
#include <string>
74
#include <typeindex>
75

76
#include <realm/array_backlink.hpp>
77
#include <realm/array_basic.hpp>
78
#include <realm/array_binary.hpp>
79
#include <realm/array_bool.hpp>
80
#include <realm/array_decimal128.hpp>
81
#include <realm/array_fixed_bytes.hpp>
82
#include <realm/array_key.hpp>
83
#include <realm/array_list.hpp>
84
#include <realm/array_mixed.hpp>
85
#include <realm/array_string.hpp>
86
#include <realm/array_timestamp.hpp>
87
#include <realm/column_integer.hpp>
88
#include <realm/column_type_traits.hpp>
89
#include <realm/index_string.hpp>
90
#include <realm/query_conditions.hpp>
91
#include <realm/query_expression.hpp>
92
#include <realm/table.hpp>
93
#include <realm/unicode.hpp>
94
#include <realm/util/serializer.hpp>
95
#include <realm/utilities.hpp>
96

97
#include <map>
98
#include <unordered_set>
99

100
#if REALM_X86_OR_X64_TRUE && defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 160040219
101
#include <immintrin.h>
102
#endif
103

104
namespace realm {
105

106
class IndexEvaluator;
107

108
class ParentNode {
109
    typedef ParentNode ThisType;
110

111
public:
112
    ParentNode() = default;
2,544,336✔
113
    virtual ~ParentNode() = default;
5,724,627✔
114

115
    virtual bool has_search_index() const
116
    {
19,932✔
117
        return false;
19,932✔
118
    }
19,932✔
119
    virtual const IndexEvaluator* index_based_keys()
120
    {
304,419✔
121
        return nullptr;
304,419✔
122
    }
304,419✔
123

124
    void gather_children(std::vector<ParentNode*>& v)
125
    {
2,246,499✔
126
        m_children.clear();
2,246,499✔
127
        size_t i = v.size();
2,246,499✔
128
        v.push_back(this);
2,246,499✔
129

130
        if (m_child)
2,246,499✔
131
            m_child->gather_children(v);
24,276✔
132

133
        m_children = v;
2,246,499✔
134
        m_children.erase(m_children.begin() + i);
2,246,499✔
135
        m_children.insert(m_children.begin(), this);
2,246,499✔
136
    }
2,246,499✔
137

138
    double cost() const
139
    {
6,964,773✔
140
        constexpr size_t bitwidth_time_unit = 64;
6,964,773✔
141
        // dt = 1/64 to 1. Match dist is 8 times more important than bitwidth
142
        return 8 * bitwidth_time_unit / m_dD + m_dT;
6,964,773✔
143
    }
6,964,773✔
144

145
    size_t find_first(size_t start, size_t end);
146

147
    bool match(const Obj& obj);
148

149
    virtual void init(bool will_query_ranges)
150
    {
2,208,546✔
151
        m_dD = 100.0;
2,208,546✔
152

153
        if (m_child)
2,208,546✔
154
            m_child->init(will_query_ranges);
24,276✔
155
    }
2,208,546✔
156

157
    void get_link_dependencies(std::vector<TableKey>& tables) const
158
    {
1,630,197✔
159
        collect_dependencies(tables);
1,630,197✔
160
        if (m_child)
1,630,197✔
161
            m_child->get_link_dependencies(tables);
24,210✔
162
    }
1,630,197✔
163

164
    void set_table(ConstTableRef table)
165
    {
3,276,024✔
166
        if (table == m_table)
3,276,024✔
167
            return;
734,772✔
168

169
        m_table = table;
2,541,252✔
170
        if (m_child)
2,541,252✔
171
            m_child->set_table(table);
3,990✔
172
        table_changed();
2,541,252✔
173
    }
2,541,252✔
174

175
    void set_cluster(const Cluster* cluster)
176
    {
7,811,724✔
177
        m_cluster = cluster;
7,811,724✔
178
        if (m_child)
7,811,724✔
179
            m_child->set_cluster(cluster);
120,075✔
180
        cluster_changed();
7,811,724✔
181
    }
7,811,724✔
182

183
    virtual void collect_dependencies(std::vector<TableKey>&) const {}
1,612,791✔
184

185
    virtual size_t find_first_local(size_t start, size_t end) = 0;
186
    virtual size_t find_all_local(size_t start, size_t end);
187

188
    virtual size_t aggregate_local(QueryStateBase* st, size_t start, size_t end, size_t local_limit,
189
                                   ArrayPayload* source_column);
190

191
    virtual std::string validate()
192
    {
6✔
193
        return m_child ? m_child->validate() : "";
6✔
194
    }
6✔
195

196
    ParentNode(const ParentNode& from);
197

198
    void add_child(std::unique_ptr<ParentNode> child)
199
    {
24,006✔
200
        if (m_child)
24,006✔
201
            m_child->add_child(std::move(child));
648✔
202
        else
23,358✔
203
            m_child = std::move(child);
23,358✔
204
    }
24,006✔
205

206
    virtual std::unique_ptr<ParentNode> clone() const = 0;
207

208
    virtual std::string describe(util::serializer::SerialisationState&) const
209
    {
×
210
        return "";
×
211
    }
×
212

213
    virtual std::string describe_condition() const
214
    {
×
215
        return "matches";
×
216
    }
×
217

218
    virtual std::string describe_expression(util::serializer::SerialisationState& state) const
219
    {
48,603✔
220
        std::string s;
48,603✔
221
        s = describe(state);
48,603✔
222
        if (m_child) {
48,603✔
223
            s = s + " and " + m_child->describe_expression(state);
936✔
224
        }
936✔
225
        return s;
48,603✔
226
    }
48,603✔
227

228
    bool consume_condition(ParentNode& other, bool ignore_indexes, std::optional<size_t> num_identical_conditions)
229
    {
708,432✔
230
        // We can only combine conditions if they're the same operator on the
231
        // same column and there's no additional conditions ANDed on
232
        if (m_condition_column_key != other.m_condition_column_key)
708,432✔
233
            return false;
3,174✔
234
        if (m_child || other.m_child)
705,258✔
235
            return false;
204✔
236
        if (typeid(*this) != typeid(other))
705,054✔
237
            return false;
30✔
238

239
        // If a search index is present, don't try to combine conditions since index search is most likely faster.
240
        // Assuming N elements to search and M conditions to check:
241
        // 1) search index present:                     O(log(N)*M)
242
        // 2) no search index, combine conditions:      O(N)
243
        // 3) no search index, conditions not combined: O(N*M)
244
        // In practice N is much larger than M, so if we have a search index, choose 1, otherwise if possible
245
        // choose 2. The exception is if we're inside a Not group or if the query is restricted to a view, as in those
246
        // cases end will always be start+1 and we'll have O(N*M) runtime even with a search index, so we want to
247
        // combine even with an index.
248
        //
249
        // If the column is not a string type, then as the number of conditions increases, the cost of using the index
250
        // for each condition rises faster than using a hash set to check if each value in a leaf matches a condition.
251
        // So if there are _many_ conditions (as defined below), combine conditions even if an index is present.
252
        if (has_search_index() && !ignore_indexes &&
705,024✔
253
            (m_condition_column_key.get_type() == col_type_String || !num_identical_conditions ||
705,024✔
254
             *num_identical_conditions < c_threshold_of_conditions_overwhelming_index))
647,082✔
255
            return false;
214,245✔
256
        return do_consume_condition(other);
490,779✔
257
    }
705,024✔
258

259
    constexpr static size_t c_threshold_of_conditions_overwhelming_index = 100;
260
    bool num_conditions_may_need_combination_counts(size_t num_total_conditions)
261
    {
27,075✔
262
        return num_total_conditions >= c_threshold_of_conditions_overwhelming_index;
27,075✔
263
    }
27,075✔
264

265
    std::unique_ptr<ParentNode> m_child;
266
    std::vector<ParentNode*> m_children;
267
    mutable ColKey m_condition_column_key = ColKey(); // Column of search criteria
268

269
    double m_dD;       // Average row distance between each local match at current position
270
    double m_dT = 1.0; // Time overhead of testing index i + 1 if we have just tested index i. > 1 for linear scans, 0
271
    // for index/tableview
272

273
    size_t m_probes = 0;
274
    size_t m_matches = 0;
275

276
protected:
277
    ConstTableRef m_table = ConstTableRef();
278
    const Cluster* m_cluster = nullptr;
279
    QueryStateBase* m_state = nullptr;
280

281
    ColumnType get_real_column_type(ColKey key)
282
    {
×
283
        return m_table.unchecked_ptr()->get_real_column_type(key);
×
284
    }
×
285

286
private:
287
    virtual void table_changed() {}
2,351,991✔
288
    virtual void cluster_changed()
289
    {
×
290
        // TODO: Should eventually be pure
291
    }
×
292
    virtual bool do_consume_condition(ParentNode&)
293
    {
32,046✔
294
        return false;
32,046✔
295
    }
32,046✔
296
};
297

298

299
class ColumnNodeBase : public ParentNode {
300
protected:
301
    ColumnNodeBase(ColKey column_key)
302
    {
1,657,587✔
303
        m_condition_column_key = column_key;
1,657,587✔
304
    }
1,657,587✔
305

306
    ColumnNodeBase(const ColumnNodeBase& from)
307
        : ParentNode(from)
873,552✔
308
    {
1,742,889✔
309
    }
1,742,889✔
310
};
311

312
class IndexEvaluator {
313
public:
314
    void init(SearchIndex* index, Mixed value);
315
    void init(std::vector<ObjKey>* storage);
316

317
    size_t do_search_index(const Cluster* cluster, size_t start, size_t end);
318

319
    size_t size() const
320
    {
14,916✔
321
        if (m_matching_keys) {
14,916✔
322
            return m_matching_keys->size();
444✔
323
        }
444✔
324
        return m_results_end - m_results_start;
14,472✔
325
    }
14,916✔
326
    ObjKey get(size_t ndx) const
327
    {
265,260✔
328
        return get_internal(ndx + m_results_start);
265,260✔
329
    }
265,260✔
330

331
private:
332
    ObjKey get_internal(size_t ndx) const
333
    {
72,421,158✔
334
        if (m_matching_keys) {
72,421,158✔
335
            return m_matching_keys->at(ndx);
606✔
336
        }
606✔
337
        if (m_index_matches) {
72,420,552✔
338
            return ObjKey(m_index_matches->get(ndx));
72,411,306✔
339
        }
72,411,306✔
340
        else if (m_results_end == 1) {
9,246✔
341
            REALM_ASSERT_EX(ndx == 0, ndx);
9,246✔
342
            return m_actual_key;
9,246✔
343
        }
9,246✔
344
        return ObjKey();
×
345
    }
72,420,552✔
346

347
    std::shared_ptr<IntegerColumn> m_index_matches;
348
    ObjKey m_actual_key;
349
    ObjKey m_last_start_key;
350
    size_t m_results_start = 0;
351
    size_t m_results_ndx = 0;
352
    size_t m_results_end = 0;
353

354
    std::vector<ObjKey>* m_matching_keys = nullptr;
355
};
356

357
template <class LeafType>
358
class IntegerNodeBase : public ColumnNodeBase {
359
public:
360
    using TConditionValue = typename LeafType::value_type;
361

362
protected:
363
    IntegerNodeBase(TConditionValue value, ColKey column_key)
364
        : ColumnNodeBase(column_key)
837,333✔
365
        , m_value(std::move(value))
825,504✔
366
    {
1,669,950✔
367
    }
1,669,950✔
368

369
    IntegerNodeBase(const IntegerNodeBase& from)
370
        : ColumnNodeBase(from)
874,290✔
371
        , m_value(from.m_value)
847,173✔
372
    {
1,744,422✔
373
    }
1,744,422✔
374

375
    void cluster_changed() override
376
    {
6,966,471✔
377
        m_leaf.emplace(m_table.unchecked_ptr()->get_alloc());
6,966,471✔
378
        m_cluster->init_leaf(this->m_condition_column_key, &*m_leaf);
6,966,471✔
379
    }
6,966,471✔
380

381
    void init(bool will_query_ranges) override
382
    {
1,683,531✔
383
        ColumnNodeBase::init(will_query_ranges);
1,683,531✔
384

385
        m_dT = .25;
1,683,531✔
386
    }
1,683,531✔
387

388
    template <class TConditionFunction>
389
    size_t find_all_local(size_t start, size_t end)
390
    {
6,221,814✔
391
        m_leaf->template find<TConditionFunction>(m_value, start, end, m_state);
6,221,814✔
392
        return end;
6,221,814✔
393
    }
6,221,814✔
394

395
    std::string describe(util::serializer::SerialisationState& state) const override
396
    {
870✔
397
        return state.describe_column(ParentNode::m_table, ColumnNodeBase::m_condition_column_key) + " " +
870✔
398
               describe_condition() + " " + util::serializer::print_value(this->m_value);
870✔
399
    }
870✔
400

401
    // Search value:
402
    TConditionValue m_value;
403

404
    // Leaf cache
405
    std::optional<LeafType> m_leaf;
406
};
407

408

409
template <class LeafType, class TConditionFunction>
410
class IntegerNode : public IntegerNodeBase<LeafType> {
411
    using BaseType = IntegerNodeBase<LeafType>;
412
    using ThisType = IntegerNode<LeafType, TConditionFunction>;
413

414
public:
415
    using TConditionValue = typename BaseType::TConditionValue;
416

417
    IntegerNode(TConditionValue value, ColKey column_key)
418
        : BaseType(value, column_key)
37,353✔
419
    {
76,845✔
420
    }
76,845✔
421
    IntegerNode(const IntegerNode& from)
422
        : BaseType(from)
59,844✔
423
    {
124,251✔
424
    }
124,251✔
425

426
    size_t find_first_local(size_t start, size_t end) override
427
    {
223,308✔
428
        return this->m_leaf->template find_first<TConditionFunction>(this->m_value, start, end);
223,308✔
429
    }
223,308✔
430

431
    size_t find_all_local(size_t start, size_t end) override
432
    {
387,165✔
433
        return BaseType::template find_all_local<TConditionFunction>(start, end);
387,165✔
434
    }
387,165✔
435

436
    std::string describe_condition() const override
437
    {
870✔
438
        return TConditionFunction::description();
870✔
439
    }
870✔
440

441
    std::unique_ptr<ParentNode> clone() const override
442
    {
124,254✔
443
        return std::unique_ptr<ParentNode>(new ThisType(*this));
124,254✔
444
    }
124,254✔
445
};
446

447
template <size_t linear_search_threshold, class LeafType, class NeedleContainer>
448
static size_t find_first_haystack(LeafType& leaf, NeedleContainer& needles, size_t start, size_t end)
449
{
1,237,227✔
450
    // for a small number of conditions, it is faster to do a linear search than to compute the hash
451
    // the exact thresholds were found experimentally
452
    if (needles.size() < linear_search_threshold) {
1,237,227✔
453
        for (size_t i = start; i < end; ++i) {
1,768,185✔
454
            auto element = leaf.get(i);
1,575,672✔
455
            if (std::find(needles.begin(), needles.end(), element) != needles.end())
1,575,672✔
456
                return i;
987,030✔
457
        }
1,575,672✔
458
    }
1,179,543✔
459
    else {
57,684✔
460
        for (size_t i = start; i < end; ++i) {
57,747✔
461
            auto element = leaf.get(i);
57,684✔
462
            if (needles.count(element))
57,684✔
463
                return i;
57,621✔
464
        }
57,684✔
465
    }
57,684✔
466
    return realm::npos;
192,576✔
467
}
1,237,227✔
468

469
template <size_t linear_search_threshold, class LeafType, class NeedleContainer>
470
static size_t find_all_haystack(LeafType& leaf, NeedleContainer& needles, size_t start, size_t end,
471
                                QueryStateBase* state)
472
{
474✔
473
    if (needles.size() < linear_search_threshold) {
474✔
474
        for (size_t i = start; i < end; ++i) {
16,932✔
475
            auto element = leaf.get(i);
16,638✔
476
            if (std::find(needles.begin(), needles.end(), element) != needles.end())
16,638✔
477
                state->match(i);
654✔
478
        }
16,638✔
479
    }
294✔
480
    else {
180✔
481
        for (size_t i = start; i < end; ++i) {
43,428✔
482
            auto element = leaf.get(i);
43,248✔
483
            if (needles.count(element))
43,248✔
484
                state->match(i);
43,206✔
485
        }
43,248✔
486
    }
180✔
487
    return end;
474✔
488
}
474✔
489

490
template <class LeafType>
491
class IntegerNode<LeafType, Equal> : public IntegerNodeBase<LeafType> {
492
public:
493
    using BaseType = IntegerNodeBase<LeafType>;
494
    using TConditionValue = typename BaseType::TConditionValue;
495
    using ThisType = IntegerNode<LeafType, Equal>;
496

497
    IntegerNode(TConditionValue value, ColKey column_key)
498
        : BaseType(value, column_key)
756,828✔
499
    {
1,549,410✔
500
    }
1,549,410✔
501
    IntegerNode(ColKey col, const Mixed* begin, const Mixed* end)
502
        : BaseType(realm::npos, col)
237✔
503
    {
462✔
504
        static_assert(is_any_v<TConditionValue, std::optional<int64_t>, int64_t>, "unexpected type change");
462✔
505
        for (const Mixed* it = begin; it != end; ++it) {
14,589✔
506
            if constexpr (std::is_same_v<TConditionValue, std::optional<int64_t>>) {
14,124✔
507
                if (it->is_null()) {
8,628✔
508
                    m_needles.insert(std::nullopt);
6✔
509
                    continue;
6✔
510
                }
6✔
511
            }
8,628✔
512
            if (const int64_t* val = it->get_if<int64_t>()) {
14,121✔
513
                m_needles.insert(*val);
14,040✔
514
            }
14,040✔
515
            else if (const double* val = it->get_if<double>()) {
81✔
516
                // JS encodes numbers as double
517
                // only add this value if it represents an integer
518
                if (*val == double(int64_t(*val))) {
18✔
519
                    m_needles.insert(int64_t(*val));
×
520
                }
×
521
            }
18✔
522
        }
11,379✔
523
    }
462✔
524

525
    void init(bool will_query_ranges) override
526
    {
1,531,035✔
527
        BaseType::init(will_query_ranges);
1,531,035✔
528
        m_nb_needles = m_needles.size();
1,531,035✔
529

530
        if (has_search_index() && m_nb_needles == 0) {
1,531,035✔
531
            SearchIndex* index = ParentNode::m_table->get_search_index(ParentNode::m_condition_column_key);
9,912✔
532
            m_index_evaluator = IndexEvaluator();
9,912✔
533
            m_index_evaluator->init(index, BaseType::m_value);
9,912✔
534
            IntegerNodeBase<LeafType>::m_dT = 0;
9,912✔
535
        }
9,912✔
536
    }
1,531,035✔
537

538
    bool do_consume_condition(ParentNode& node) override
539
    {
30,441✔
540
        auto& other = static_cast<ThisType&>(node);
30,441✔
541
        REALM_ASSERT(this->m_condition_column_key == other.m_condition_column_key);
30,441✔
542
        if (m_needles.empty()) {
30,441✔
543
            m_needles.insert(this->m_value);
24,429✔
544
        }
24,429✔
545
        if (other.m_needles.empty()) {
30,441✔
546
            m_needles.insert(other.m_value);
30,426✔
547
        }
30,426✔
548
        else {
15✔
549
            for (const auto& val : other.m_needles) {
15✔
550
                m_needles.insert(val);
12✔
551
            }
12✔
552
        }
15✔
553
        return true;
30,441✔
554
    }
30,441✔
555

556
    bool has_search_index() const override
557
    {
1,573,404✔
558
        return this->m_table->search_index_type(IntegerNodeBase<LeafType>::m_condition_column_key) ==
1,573,404✔
559
               IndexType::General;
1,573,404✔
560
    }
1,573,404✔
561

562
    const IndexEvaluator* index_based_keys() override
563
    {
1,515,738✔
564
        return m_index_evaluator ? &(*m_index_evaluator) : nullptr;
1,515,738✔
565
    }
1,515,738✔
566

567
    size_t find_first_local(size_t start, size_t end) override
568
    {
4,468,098✔
569
        REALM_ASSERT(this->m_table);
4,468,098✔
570
        size_t s = realm::npos;
4,468,098✔
571

572
        if (start < end) {
4,468,098✔
573
            if (m_nb_needles) {
4,464,198✔
574
                s = find_first_haystack<22>(*this->m_leaf, m_needles, start, end);
1,214,031✔
575
            }
1,214,031✔
576
            else if (m_index_evaluator) {
3,250,167✔
577
                return m_index_evaluator->do_search_index(BaseType::m_cluster, start, end);
1,764✔
578
            }
1,764✔
579
            else if (end - start == 1) {
3,248,403✔
580
                if (this->m_leaf->get(start) == this->m_value) {
1,292,817✔
581
                    s = start;
562,428✔
582
                }
562,428✔
583
            }
1,292,817✔
584
            else {
1,955,586✔
585
                s = this->m_leaf->template find_first<Equal>(this->m_value, start, end);
1,955,586✔
586
            }
1,955,586✔
587
        }
4,464,198✔
588

589
        return s;
4,466,334✔
590
    }
4,468,098✔
591

592
    size_t find_all_local(size_t start, size_t end) override
593
    {
5,501,490✔
594
        if (m_nb_needles) {
5,501,490✔
595
            return find_all_haystack<22>(*this->m_leaf, m_needles, start, end, ParentNode::m_state);
474✔
596
        }
474✔
597
        return BaseType::template find_all_local<Equal>(start, end);
5,501,016✔
598
    }
5,501,490✔
599

600
    std::string describe(util::serializer::SerialisationState& state) const override
601
    {
12,942✔
602
        REALM_ASSERT(this->m_condition_column_key);
12,942✔
603
        std::string col_descr = state.describe_column(this->m_table, this->m_condition_column_key);
12,942✔
604

605
        if (m_needles.empty()) {
12,942✔
606
            return col_descr + " " + Equal::description() + " " +
12,768✔
607
                   util::serializer::print_value(IntegerNodeBase<LeafType>::m_value);
12,768✔
608
        }
12,768✔
609

610
        std::string list_contents;
174✔
611
        bool is_first = true;
174✔
612
        for (auto it : m_needles) {
378✔
613
            list_contents +=
378✔
614
                util::format("%1%2", is_first ? "" : ", ", util::serializer::print_value(it)); // "it" may be null
378✔
615
            is_first = false;
378✔
616
        }
378✔
617
        std::string desc = util::format("%1 IN {%2}", col_descr, list_contents);
174✔
618
        return desc;
174✔
619
    }
12,942✔
620

621
    std::unique_ptr<ParentNode> clone() const override
622
    {
1,466,178✔
623
        return std::unique_ptr<ParentNode>(new ThisType(*this));
1,466,178✔
624
    }
1,466,178✔
625

626
private:
627
    std::unordered_set<TConditionValue> m_needles;
628
    size_t m_nb_needles = 0;
629
    std::optional<IndexEvaluator> m_index_evaluator;
630

631
    IntegerNode(const IntegerNode<LeafType, Equal>& from)
632
        : BaseType(from)
777,849✔
633
        , m_needles(from.m_needles)
777,849✔
634
    {
1,582,998✔
635
    }
1,582,998✔
636
};
637

638

639
// This node is currently used for floats and doubles only
640
template <class LeafType, class TConditionFunction>
641
class FloatDoubleNode : public ParentNode {
642
public:
643
    using TConditionValue = typename LeafType::value_type;
644
    static const bool special_null_node = false;
645

646
    FloatDoubleNode(TConditionValue v, ColKey column_key)
647
        : m_value(v)
10,860✔
648
    {
21,720✔
649
        m_condition_column_key = column_key;
21,720✔
650
        m_dT = 1.0;
21,720✔
651
    }
21,720✔
652
    FloatDoubleNode(null, ColKey column_key)
653
        : m_value(null::get_null_float<TConditionValue>())
300✔
654
    {
600✔
655
        m_condition_column_key = column_key;
600✔
656
        m_dT = 1.0;
600✔
657
    }
600✔
658

659
    void cluster_changed() override
660
    {
38,292✔
661
        m_leaf.emplace(m_table.unchecked_ptr()->get_alloc());
38,292✔
662
        m_cluster->init_leaf(this->m_condition_column_key, &*m_leaf);
38,292✔
663
    }
38,292✔
664

665
    size_t find_first_local(size_t start, size_t end) override
666
    {
375,528✔
667
        TConditionFunction cond;
375,528✔
668

669
        auto find = [&](bool nullability) {
375,528✔
670
            bool value_nan = nullability ? null::is_null_float(m_value) : false;
375,528!
671
            for (size_t s = start; s < end; ++s) {
3,086,232!
672
                TConditionValue v = m_leaf->get(s);
3,004,368✔
673
                REALM_ASSERT(!(null::is_null_float(v) && !nullability));
3,004,368!
674
                if (cond(v, m_value, nullability ? null::is_null_float<TConditionValue>(v) : false, value_nan))
3,004,368!
675
                    return s;
293,664✔
676
            }
3,004,368✔
677
            return not_found;
81,864✔
678
        };
375,528✔
679

680
        // This will inline the second case but no the first. Todo, use templated lambda when switching to c++14
681
        if (m_table->is_nullable(m_condition_column_key))
375,528!
682
            return find(true);
20,832✔
683
        else
354,696✔
684
            return find(false);
354,696✔
685
    }
375,528✔
686

687
    std::string describe(util::serializer::SerialisationState& state) const override
688
    {
270✔
689
        REALM_ASSERT(m_condition_column_key);
270!
690
        return state.describe_column(ParentNode::m_table, m_condition_column_key) + " " + describe_condition() + " " +
270✔
691
               util::serializer::print_value(FloatDoubleNode::m_value);
270✔
692
    }
270✔
693
    std::string describe_condition() const override
694
    {
270✔
695
        return TConditionFunction::description();
270✔
696
    }
270✔
697

698
    std::unique_ptr<ParentNode> clone() const override
699
    {
21,210✔
700
        return std::unique_ptr<ParentNode>(new FloatDoubleNode(*this));
21,210✔
701
    }
21,210✔
702

703
    FloatDoubleNode(const FloatDoubleNode& from)
704
        : ParentNode(from)
10,605✔
705
        , m_value(from.m_value)
10,605✔
706
    {
21,210✔
707
    }
21,210✔
708

709
protected:
710
    TConditionValue m_value;
711
    std::optional<LeafType> m_leaf;
712
};
713

714
template <class T, class TConditionFunction>
715
class SizeNode : public ParentNode {
716
public:
717
    SizeNode(int64_t v, ColKey column)
718
        : m_value(v)
6✔
719
    {
12✔
720
        m_condition_column_key = column;
12✔
721
        m_dT = 20.0;
12✔
722
    }
12✔
723

724
    void cluster_changed() override
725
    {
12✔
726
        m_leaf.emplace(m_table.unchecked_ptr()->get_alloc());
12✔
727
        m_cluster->init_leaf(m_condition_column_key, &*m_leaf);
12✔
728
    }
12✔
729

730
    size_t find_first_local(size_t start, size_t end) override
731
    {
18✔
732
        for (size_t s = start; s < end; ++s) {
78!
733
            if (T v = m_leaf->get(s)) {
72!
734
                int64_t sz = v.size();
24✔
735
                if (TConditionFunction()(sz, m_value))
24!
736
                    return s;
12✔
737
            }
24✔
738
        }
72✔
739
        return not_found;
6✔
740
    }
18✔
741

742
    std::unique_ptr<ParentNode> clone() const override
743
    {
18✔
744
        return std::unique_ptr<ParentNode>(new SizeNode(*this));
18✔
745
    }
18✔
746

747
    SizeNode(const SizeNode& from)
748
        : ParentNode(from)
9✔
749
        , m_value(from.m_value)
9✔
750
    {
18✔
751
    }
18✔
752

753
private:
754
    using LeafType = typename ColumnTypeTraits<T>::cluster_leaf_type;
755
    std::optional<LeafType> m_leaf;
756
    int64_t m_value;
757
};
758

759
extern size_t size_of_list_from_ref(ref_type ref, Allocator& alloc, ColumnType col_type, bool nullable);
760

761
template <class TConditionFunction>
762
class SizeListNode : public ParentNode {
763
public:
764
    SizeListNode(int64_t v, ColKey column)
765
        : m_value(v)
72✔
766
    {
144✔
767
        m_condition_column_key = column;
144✔
768
        m_dT = 30.0;
144✔
769
    }
144✔
770

771
    void reset_cache()
772
    {
288✔
773
        m_cached_col_type = m_condition_column_key.get_type();
288✔
774
        m_cached_nullable = m_condition_column_key.is_nullable();
288✔
775
        REALM_ASSERT_DEBUG(m_condition_column_key.is_list());
288✔
776
    }
288✔
777

778
    void cluster_changed() override
779
    {
144✔
780
        m_leaf.emplace(m_table.unchecked_ptr()->get_alloc());
144✔
781
        m_cluster->init_leaf(m_condition_column_key, &*m_leaf);
144✔
782
        reset_cache();
144✔
783
    }
144✔
784

785
    void init(bool will_query_ranges) override
786
    {
144✔
787
        ParentNode::init(will_query_ranges);
144✔
788
        reset_cache();
144✔
789
    }
144✔
790

791
    size_t find_first_local(size_t start, size_t end) override
792
    {
204✔
793
        Allocator& alloc = m_table.unchecked_ptr()->get_alloc();
204✔
794
        for (size_t s = start; s < end; ++s) {
294✔
795
            if (ref_type ref = m_leaf->get(s)) {
276!
796
                int64_t sz = size_of_list_from_ref(ref, alloc, m_cached_col_type, m_cached_nullable);
204✔
797
                if (TConditionFunction()(sz, m_value))
204✔
798
                    return s;
186✔
799
            }
204✔
800
        }
276✔
801
        return not_found;
18✔
802
    }
204✔
803

804
    std::unique_ptr<ParentNode> clone() const override
805
    {
162✔
806
        return std::unique_ptr<ParentNode>(new SizeListNode(*this));
162✔
807
    }
162✔
808

809
    SizeListNode(const SizeListNode& from)
810
        : ParentNode(from)
81✔
811
        , m_value(from.m_value)
81✔
812
    {
162✔
813
    }
162✔
814

815
private:
816
    std::optional<ArrayList> m_leaf;
817

818
    int64_t m_value;
819

820
    ColumnType m_cached_col_type;
821
    bool m_cached_nullable;
822
};
823

824

825
template <class TConditionFunction>
826
class BinaryNode : public ParentNode {
827
public:
828
    using TConditionValue = BinaryData;
829
    static const bool special_null_node = false;
830

831
    BinaryNode(BinaryData v, ColKey column)
832
        : m_value(v)
6,141✔
833
    {
12,282✔
834
        m_condition_column_key = column;
12,282✔
835
        m_dT = 100.0;
12,282✔
836
    }
12,282✔
837

838
    BinaryNode(null, ColKey column)
839
        : BinaryNode(BinaryData{}, column)
201✔
840
    {
402✔
841
    }
402✔
842

843
    void cluster_changed() override
844
    {
17,592✔
845
        m_leaf.emplace(m_table.unchecked_ptr()->get_alloc());
17,592✔
846
        m_cluster->init_leaf(m_condition_column_key, &*m_leaf);
17,592✔
847
    }
17,592✔
848

849
    size_t find_first_local(size_t start, size_t end) override
850
    {
67,020✔
851
        TConditionFunction condition;
67,020✔
852
        for (size_t s = start; s < end; ++s) {
2,540,466!
853
            BinaryData value = m_leaf->get(s);
2,525,232✔
854
            if (condition(m_value.get(), value))
2,525,232!
855
                return s;
51,786✔
856
        }
2,525,232✔
857
        return not_found;
15,234✔
858
    }
67,020✔
859

860
    std::string describe(util::serializer::SerialisationState& state) const override
861
    {
2,286✔
862
        REALM_ASSERT(m_condition_column_key);
2,286!
863
        return state.describe_column(ParentNode::m_table, m_condition_column_key) + " " +
2,286✔
864
               TConditionFunction::description() + " " + util::serializer::print_value(BinaryNode::m_value.get());
2,286✔
865
    }
2,286✔
866

867
    std::unique_ptr<ParentNode> clone() const override
868
    {
14,262✔
869
        return std::unique_ptr<ParentNode>(new BinaryNode(*this));
14,262✔
870
    }
14,262✔
871

872
    BinaryNode(const BinaryNode& from)
873
        : ParentNode(from)
7,131✔
874
        , m_value(from.m_value)
7,131✔
875
    {
14,262✔
876
    }
14,262✔
877

878
private:
879
    OwnedBinaryData m_value;
880
    std::optional<ArrayBinary> m_leaf;
881
};
882

883
template <class TConditionFunction>
884
class BoolNode : public ParentNode {
885
public:
886
    using TConditionValue = bool;
887

888
    BoolNode(util::Optional<bool> v, ColKey column)
889
        : m_value(v)
1,035✔
890
    {
2,070✔
891
        m_condition_column_key = column;
2,070✔
892
    }
2,070✔
893

894
    BoolNode(const BoolNode& from)
895
        : ParentNode(from)
1,008✔
896
        , m_value(from.m_value)
1,008✔
897
        , m_index_evaluator(from.m_index_evaluator)
1,008✔
898
    {
2,016✔
899
    }
2,016✔
900

901
    void init(bool will_query_ranges) override
902
    {
3,126✔
903
        ParentNode::init(will_query_ranges);
3,126✔
904

905
        if constexpr (std::is_same_v<TConditionFunction, Equal>) {
3,126✔
906
            if (m_index_evaluator) {
1,890✔
907
                SearchIndex* index = m_table->get_search_index(m_condition_column_key);
×
908
                m_index_evaluator->init(index, m_value);
×
909
                this->m_dT = 0;
×
910
            }
×
911
        }
1,890✔
912
    }
3,126✔
913

914
    void table_changed() override
915
    {
2,538✔
916
        if constexpr (std::is_same_v<TConditionFunction, Equal>) {
2,538✔
917
            const bool has_index = m_table->search_index_type(m_condition_column_key) == IndexType::General;
1,482✔
918
            m_index_evaluator = has_index ? std::make_optional(IndexEvaluator{}) : std::nullopt;
1,482✔
919
        }
1,482✔
920
    }
2,538✔
921

922
    const IndexEvaluator* index_based_keys() override
923
    {
2,964✔
924
        return m_index_evaluator ? &(*m_index_evaluator) : nullptr;
2,964!
925
    }
2,964✔
926

927
    bool has_search_index() const override
928
    {
60✔
929
        return bool(m_index_evaluator);
60✔
930
    }
60✔
931

932
    void cluster_changed() override
933
    {
3,480✔
934
        m_leaf.emplace(m_table.unchecked_ptr()->get_alloc());
3,480✔
935
        m_cluster->init_leaf(m_condition_column_key, &*m_leaf);
3,480✔
936
    }
3,480✔
937

938
    size_t find_first_local(size_t start, size_t end) override
939
    {
31,236✔
940
        if constexpr (std::is_same_v<TConditionFunction, Equal>) {
31,236✔
941
            if (m_index_evaluator) {
29,964✔
942
                return m_index_evaluator->do_search_index(m_cluster, start, end);
×
943
            }
×
944
        }
29,964✔
945

946
        TConditionFunction condition;
29,964✔
947
        bool m_value_is_null = !m_value;
30,600✔
948
        for (size_t s = start; s < end; ++s) {
56,664!
949
            auto value = m_leaf->get(s);
54,942✔
950
            if (condition(value, m_value, !value, m_value_is_null))
54,942!
951
                return s;
29,514✔
952
        }
54,942✔
953
        return not_found;
1,722✔
954
    }
30,600✔
955

956
    std::string describe(util::serializer::SerialisationState& state) const override
957
    {
96✔
958
        return state.describe_column(ParentNode::m_table, m_condition_column_key) + " " +
96✔
959
               TConditionFunction::description() + " " + util::serializer::print_value(m_value);
96✔
960
    }
96✔
961

962
    std::unique_ptr<ParentNode> clone() const override
963
    {
2,016✔
964
        return std::unique_ptr<ParentNode>(new BoolNode(*this));
2,016✔
965
    }
2,016✔
966

967
private:
968
    std::optional<bool> m_value;
969
    std::optional<ArrayBoolNull> m_leaf;
970
    std::optional<IndexEvaluator> m_index_evaluator;
971
};
972

973
class TimestampNodeBase : public ParentNode {
974
public:
975
    using TConditionValue = Timestamp;
976
    static const bool special_null_node = false;
977

978
    TimestampNodeBase(Timestamp v, ColKey column)
979
        : m_value(v)
6,723✔
980
    {
13,446✔
981
        m_condition_column_key = column;
13,446✔
982
        m_dT = 2.0;
13,446✔
983
    }
13,446✔
984

985
    TimestampNodeBase(null, ColKey column)
986
        : TimestampNodeBase(Timestamp{}, column)
144✔
987
    {
288✔
988
    }
288✔
989

990
    void cluster_changed() override
991
    {
15,810✔
992
        m_leaf.emplace(m_table.unchecked_ptr()->get_alloc());
15,810✔
993
        m_cluster->init_leaf(m_condition_column_key, &*m_leaf);
15,810✔
994
    }
15,810✔
995

996
protected:
997
    TimestampNodeBase(const TimestampNodeBase& from)
998
        : ParentNode(from)
6,360✔
999
        , m_value(from.m_value)
6,360✔
1000
    {
12,720✔
1001
    }
12,720✔
1002

1003
    Timestamp m_value;
1004
    std::optional<ArrayTimestamp> m_leaf;
1005
};
1006

1007
template <class TConditionFunction>
1008
class TimestampNode : public TimestampNodeBase {
1009
public:
1010
    using TimestampNodeBase::TimestampNodeBase;
1011

1012
    void init(bool will_query_ranges) override
1013
    {
15,942✔
1014
        TimestampNodeBase::init(will_query_ranges);
15,942✔
1015

1016
        if constexpr (std::is_same_v<TConditionFunction, Equal>) {
15,942✔
1017
            if (m_index_evaluator) {
9,378✔
1018
                SearchIndex* index =
2,592✔
1019
                    TimestampNodeBase::m_table->get_search_index(TimestampNodeBase::m_condition_column_key);
2,592✔
1020
                m_index_evaluator->init(index, TimestampNodeBase::m_value);
2,592✔
1021
                this->m_dT = 0;
2,592✔
1022
            }
2,592✔
1023
        }
9,378✔
1024
    }
15,942✔
1025

1026
    void table_changed() override
1027
    {
14,814✔
1028
        if constexpr (std::is_same_v<TConditionFunction, Equal>) {
14,814✔
1029
            const bool has_index =
9,180✔
1030
                this->m_table->search_index_type(TimestampNodeBase::m_condition_column_key) == IndexType::General;
9,180✔
1031
            m_index_evaluator = has_index ? std::make_optional(IndexEvaluator{}) : std::nullopt;
9,180✔
1032
        }
9,180✔
1033
    }
14,814✔
1034

1035
    const IndexEvaluator* index_based_keys() override
1036
    {
8,436✔
1037
        return m_index_evaluator ? &*m_index_evaluator : nullptr;
8,436✔
1038
    }
8,436✔
1039

1040
    bool has_search_index() const override
1041
    {
7,242✔
1042
        return bool(m_index_evaluator);
7,242✔
1043
    }
7,242✔
1044

1045
    size_t find_first_local(size_t start, size_t end) override
1046
    {
59,040✔
1047
        if constexpr (std::is_same_v<TConditionFunction, Equal>) {
59,040✔
1048
            if (m_index_evaluator) {
21,024✔
1049
                return m_index_evaluator->do_search_index(this->m_cluster, start, end);
4,902✔
1050
            }
4,902✔
1051
        }
21,024✔
1052
        return m_leaf->find_first<TConditionFunction>(m_value, start, end);
16,122✔
1053
    }
59,040✔
1054

1055
    std::string describe(util::serializer::SerialisationState& state) const override
1056
    {
156✔
1057
        REALM_ASSERT(m_condition_column_key);
156!
1058
        return state.describe_column(ParentNode::m_table, m_condition_column_key) + " " +
156✔
1059
               TConditionFunction::description() + " " + util::serializer::print_value(TimestampNode::m_value);
156✔
1060
    }
156✔
1061

1062
    std::unique_ptr<ParentNode> clone() const override
1063
    {
12,720✔
1064
        return std::unique_ptr<ParentNode>(new TimestampNode(*this));
12,720✔
1065
    }
12,720✔
1066

1067
protected:
1068
    std::optional<IndexEvaluator> m_index_evaluator;
1069
};
1070

1071
class DecimalNodeBase : public ParentNode {
1072
public:
1073
    using TConditionValue = Decimal128;
1074
    static const bool special_null_node = false;
1075

1076
    DecimalNodeBase(Decimal128 v, ColKey column)
1077
        : m_value(v)
3,117✔
1078
    {
6,234✔
1079
        m_condition_column_key = column;
6,234✔
1080
    }
6,234✔
1081

1082
    DecimalNodeBase(null, ColKey column)
1083
        : DecimalNodeBase(Decimal128{null()}, column)
6✔
1084
    {
12✔
1085
    }
12✔
1086

1087
    void cluster_changed() override
1088
    {
6,228✔
1089
        m_leaf.emplace(m_table.unchecked_ptr()->get_alloc());
6,228✔
1090
        m_cluster->init_leaf(m_condition_column_key, &*m_leaf);
6,228✔
1091
    }
6,228✔
1092

1093
    void init(bool will_query_ranges) override
1094
    {
6,228✔
1095
        ParentNode::init(will_query_ranges);
6,228✔
1096

1097
        m_dD = 100.0;
6,228✔
1098
    }
6,228✔
1099

1100
protected:
1101
    DecimalNodeBase(const DecimalNodeBase& from)
1102
        : ParentNode(from)
3,780✔
1103
        , m_value(from.m_value)
3,780✔
1104
    {
7,560✔
1105
    }
7,560✔
1106

1107
    Decimal128 m_value;
1108
    std::optional<ArrayDecimal128> m_leaf;
1109
};
1110

1111
template <class TConditionFunction>
1112
class DecimalNode : public DecimalNodeBase {
1113
public:
1114
    using DecimalNodeBase::DecimalNodeBase;
1115

1116
    size_t find_first_local(size_t start, size_t end) override
1117
    {
14,526✔
1118
        TConditionFunction cond;
14,526✔
1119
        bool value_is_null = m_value.is_null();
14,526✔
1120
        for (size_t i = start; i < end; i++) {
1,016,136!
1121
            Decimal128 val = m_leaf->get(i);
1,009,866✔
1122
            if (cond(val, m_value, val.is_null(), value_is_null))
1,009,866!
1123
                return i;
8,256✔
1124
        }
1,009,866✔
1125
        return realm::npos;
6,270✔
1126
    }
14,526✔
1127

1128
    std::string describe(util::serializer::SerialisationState& state) const override
1129
    {
612✔
1130
        REALM_ASSERT(m_condition_column_key);
612!
1131
        return state.describe_column(ParentNode::m_table, m_condition_column_key) + " " +
612✔
1132
               TConditionFunction::description() + " " + util::serializer::print_value(DecimalNode::m_value);
612✔
1133
    }
612✔
1134

1135
    std::unique_ptr<ParentNode> clone() const override
1136
    {
7,560✔
1137
        return std::unique_ptr<ParentNode>(new DecimalNode(*this));
7,560✔
1138
    }
7,560✔
1139
};
1140

1141
template <class ObjectType, class ArrayType>
1142
class FixedBytesNodeBase : public ParentNode {
1143
public:
1144
    using TConditionValue = ObjectType;
1145
    static const bool special_null_node = false;
1146

1147
    FixedBytesNodeBase(ObjectType v, ColKey column)
1148
        : m_value(v)
322,485✔
1149
    {
644,970✔
1150
        m_condition_column_key = column;
644,970✔
1151
    }
644,970✔
1152

1153
    FixedBytesNodeBase(null, ColKey column)
1154
        : FixedBytesNodeBase(ObjectType{}, column)
183✔
1155
    {
366✔
1156
        m_value_is_null = true;
366✔
1157
    }
366✔
1158

1159
    void cluster_changed() override
1160
    {
216,723✔
1161
        m_leaf.emplace(m_table.unchecked_ptr()->get_alloc());
216,723✔
1162
        m_cluster->init_leaf(m_condition_column_key, &*m_leaf);
216,723✔
1163
    }
216,723✔
1164

1165
    void init(bool will_query_ranges) override
1166
    {
216,591✔
1167
        ParentNode::init(will_query_ranges);
216,591✔
1168

1169
        m_dD = 100.0;
216,591✔
1170
    }
216,591✔
1171

1172
protected:
1173
    FixedBytesNodeBase(const FixedBytesNodeBase& from)
1174
        : ParentNode(from)
643,686✔
1175
        , m_value(from.m_value)
643,686✔
1176
        , m_value_is_null(from.m_value_is_null)
643,686✔
1177
    {
1,287,372✔
1178
    }
1,287,372✔
1179

1180
    ObjectType m_value;
1181
    std::optional<ArrayType> m_leaf;
1182
    bool m_value_is_null = false;
1183
};
1184

1185
template <class TConditionFunction, class ObjectType, class ArrayType>
1186
class FixedBytesNode : public FixedBytesNodeBase<ObjectType, ArrayType> {
1187
public:
1188
    using FixedBytesNodeBase<ObjectType, ArrayType>::FixedBytesNodeBase;
1189

1190
    size_t find_first_local(size_t start, size_t end) override
1191
    {
100,812✔
1192
        TConditionFunction cond;
100,812✔
1193
        for (size_t i = start; i < end; i++) {
208,584!
1194
            util::Optional<ObjectType> val = this->m_leaf->get(i);
207,696✔
1195
            if (val) {
207,696!
1196
                if (cond(*val, this->m_value, false, this->m_value_is_null))
172,524!
1197
                    return i;
88,248✔
1198
            }
172,524✔
1199
            else {
35,172✔
1200
                if (cond(ObjectType{}, this->m_value, true, this->m_value_is_null))
35,172!
1201
                    return i;
11,676✔
1202
            }
35,172✔
1203
        }
207,696✔
1204
        return realm::npos;
888✔
1205
    }
100,812✔
1206

1207
    std::string describe(util::serializer::SerialisationState& state) const override
1208
    {
396✔
1209
        REALM_ASSERT(this->m_condition_column_key);
396!
1210
        return state.describe_column(ParentNode::m_table, this->m_condition_column_key) + " " +
396✔
1211
               TConditionFunction::description() + " " +
396✔
1212
               (this->m_value_is_null ? util::serializer::print_value(realm::null())
396!
1213
                                      : util::serializer::print_value(this->m_value));
396✔
1214
    }
396✔
1215

1216
    std::unique_ptr<ParentNode> clone() const override
1217
    {
1,632✔
1218
        return std::unique_ptr<ParentNode>(new FixedBytesNode(*this));
1,632✔
1219
    }
1,632✔
1220
};
1221

1222
template <class ObjectType, class ArrayType>
1223
class FixedBytesNode<Equal, ObjectType, ArrayType> : public FixedBytesNodeBase<ObjectType, ArrayType> {
1224
public:
1225
    using FixedBytesNodeBase<ObjectType, ArrayType>::FixedBytesNodeBase;
1226
    using BaseType = FixedBytesNodeBase<ObjectType, ArrayType>;
1227
    using ThisType = FixedBytesNode<Equal, ObjectType, ArrayType>;
1228

1229
    FixedBytesNode(ColKey col, const Mixed* begin, const Mixed* end)
1230
        : BaseType(realm::null(), col)
144✔
1231
    {
288✔
1232
        REALM_ASSERT(begin && end);
288✔
1233
        REALM_ASSERT(begin != end);
288✔
1234
        for (const Mixed* val = begin; val != end; ++val) {
15,120✔
1235
            if (val->is_null()) {
14,832✔
1236
                m_needles.insert(std::nullopt);
36✔
1237
                continue;
36✔
1238
            }
36✔
1239
            if (const ObjectType* v = val->get_if<ObjectType>()) {
14,796✔
1240
                m_needles.insert({*v});
14,742✔
1241
            }
14,742✔
1242
        }
14,796✔
1243
        if (m_needles.size() == 0) {
288✔
1244
            throw InvalidArgument("No arguments to compare to");
×
1245
        }
×
1246
    }
288✔
1247

1248
    void init(bool will_query_ranges) override
1249
    {
215,595✔
1250
        BaseType::init(will_query_ranges);
215,595✔
1251
        m_nb_needles = m_needles.size();
215,595✔
1252

1253
        if (!this->m_value_is_null) {
215,595✔
1254
            m_optional_value = this->m_value;
215,253✔
1255
        }
215,253✔
1256
        if (m_nb_needles == 0 && has_search_index()) {
215,595✔
1257
            m_index_evaluator = std::make_optional(IndexEvaluator{});
214,827✔
1258
            SearchIndex* index = BaseType::m_table->get_search_index(BaseType::m_condition_column_key);
214,827✔
1259
            m_index_evaluator->init(index, m_optional_value);
214,827✔
1260
            this->m_dT = 0;
214,827✔
1261
        }
214,827✔
1262
    }
215,595✔
1263

1264
    const IndexEvaluator* index_based_keys() override
1265
    {
1,308✔
1266
        return m_index_evaluator ? &(*m_index_evaluator) : nullptr;
1,308✔
1267
    }
1,308✔
1268

1269
    bool has_search_index() const override
1270
    {
857,439✔
1271
        return this->m_table->search_index_type(BaseType::m_condition_column_key) == IndexType::General;
857,439✔
1272
    }
857,439✔
1273

1274
    size_t find_first_local(size_t start, size_t end) override
1275
    {
242,361✔
1276
        REALM_ASSERT(this->m_table);
242,361✔
1277
        size_t s = realm::npos;
242,361✔
1278

1279
        if (start < end) {
242,361✔
1280
            if (m_nb_needles) {
242,361✔
1281
                return find_first_haystack<22>(*this->m_leaf, m_needles, start, end);
14,952✔
1282
            }
14,952✔
1283
            if (m_index_evaluator) {
227,409✔
1284
                return m_index_evaluator->do_search_index(this->m_cluster, start, end);
214,167✔
1285
            }
214,167✔
1286

1287
            if (end - start == 1) {
13,242✔
1288
                if (this->m_leaf->get(start) == m_optional_value) {
138✔
1289
                    s = start;
72✔
1290
                }
72✔
1291
            }
138✔
1292
            else {
13,104✔
1293
                s = this->m_leaf->find_first(m_optional_value, start, end);
13,104✔
1294
            }
13,104✔
1295
        }
13,242✔
1296

1297
        return s;
13,242✔
1298
    }
242,361✔
1299

1300
    bool do_consume_condition(ParentNode& node) override
1301
    {
428,061✔
1302
        auto& other = static_cast<ThisType&>(node);
428,061✔
1303
        REALM_ASSERT(this->m_condition_column_key == other.m_condition_column_key);
428,061✔
1304
        if (m_needles.empty()) {
428,061✔
1305
            m_needles.insert(this->m_value_is_null ? std::nullopt : std::make_optional(this->m_value));
12✔
1306
        }
12✔
1307
        if (other.m_needles.empty()) {
428,061✔
1308
            m_needles.insert(other.m_value_is_null ? std::nullopt : std::make_optional(other.m_value));
428,037!
1309
        }
428,037✔
1310
        else {
24✔
1311
            for (const auto& val : other.m_needles) {
24✔
1312
                m_needles.insert(val);
24✔
1313
            }
24✔
1314
        }
24✔
1315
        return true;
428,061✔
1316
    }
428,061✔
1317

1318
    std::string describe(util::serializer::SerialisationState& state) const override
1319
    {
660✔
1320
        REALM_ASSERT(this->m_condition_column_key);
660✔
1321
        std::string col_descr = state.describe_column(this->m_table, this->m_condition_column_key);
660✔
1322
        if (m_needles.empty()) {
660✔
1323
            return util::format("%1 %2 %3", col_descr, Equal::description(),
660✔
1324
                                (this->m_value_is_null ? util::serializer::print_value(realm::null())
660✔
1325
                                                       : util::serializer::print_value(this->m_value)));
660✔
1326
        }
660✔
1327
        std::string list_contents;
×
1328
        bool is_first = true;
×
1329
        for (auto it : m_needles) {
×
1330
            list_contents +=
×
1331
                util::format("%1%2", is_first ? "" : ", ", util::serializer::print_value(it)); // "it" may be null
×
1332
            is_first = false;
×
1333
        }
×
1334
        return util::format("%1 IN {%2}", col_descr, list_contents);
×
1335
    }
660✔
1336

1337
    std::unique_ptr<ParentNode> clone() const override
1338
    {
1,285,740✔
1339
        return std::unique_ptr<ParentNode>(new FixedBytesNode(*this));
1,285,740✔
1340
    }
1,285,740✔
1341

1342
protected:
1343
    std::optional<ObjectType> m_optional_value;
1344
    std::optional<IndexEvaluator> m_index_evaluator;
1345
    std::unordered_set<std::optional<ObjectType>> m_needles;
1346
    size_t m_nb_needles = 0;
1347
};
1348

1349

1350
template <typename T>
1351
using ObjectIdNode = FixedBytesNode<T, ObjectId, ArrayObjectIdNull>;
1352
template <typename T>
1353
using UUIDNode = FixedBytesNode<T, UUID, ArrayUUIDNull>;
1354

1355
class MixedNodeBase : public ParentNode {
1356
public:
1357
    using TConditionValue = Mixed;
1358
    static const bool special_null_node = false;
1359

1360
    MixedNodeBase(Mixed v, ColKey column)
1361
        : m_value(v)
3,183✔
1362
        , m_value_is_null(v.is_null())
3,183✔
1363
    {
6,366✔
1364
        REALM_ASSERT(column.get_type() == col_type_Mixed);
6,366✔
1365
        get_ownership();
6,366✔
1366
        m_condition_column_key = column;
6,366✔
1367
    }
6,366✔
1368

1369
    MixedNodeBase(null, ColKey column)
1370
        : MixedNodeBase(Mixed{}, column)
1371
    {
×
1372
        m_value_is_null = true;
×
1373
    }
×
1374

1375
    void cluster_changed() override
1376
    {
3,450✔
1377
        m_leaf.emplace(m_table.unchecked_ptr()->get_alloc());
3,450✔
1378
        m_cluster->init_leaf(m_condition_column_key, &*m_leaf);
3,450✔
1379
    }
3,450✔
1380

1381
    void init(bool will_query_ranges) override
1382
    {
6,378✔
1383
        ParentNode::init(will_query_ranges);
6,378✔
1384

1385
        m_dD = 100.0;
6,378✔
1386
    }
6,378✔
1387

1388
    std::string describe(util::serializer::SerialisationState& state) const override
1389
    {
246✔
1390
        REALM_ASSERT(m_condition_column_key);
246✔
1391
        std::string value;
246✔
1392
        if (m_value.is_type(type_TypedLink)) {
246✔
1393
            value = util::serializer::print_value(m_value.get<ObjLink>(), state.group);
12✔
1394
        }
12✔
1395
        else {
234✔
1396
            value = util::serializer::print_value(m_value);
234✔
1397
        }
234✔
1398
        return state.describe_column(ParentNode::m_table, m_condition_column_key) + " " + this->describe_condition() +
246✔
1399
               " " + value;
246✔
1400
    }
246✔
1401

1402
protected:
1403
    MixedNodeBase(const MixedNodeBase& from)
1404
        : ParentNode(from)
3,390✔
1405
        , m_value(from.m_value)
3,390✔
1406
        , m_value_is_null(from.m_value_is_null)
3,390✔
1407
    {
6,780✔
1408
        get_ownership();
6,780✔
1409
    }
6,780✔
1410

1411
    void get_ownership()
1412
    {
13,146✔
1413
        if (m_value.is_type(type_String, type_Binary)) {
13,146✔
1414
            auto bin = m_value.export_to_type<BinaryData>();
3,036✔
1415
            m_buffer = OwnedBinaryData(bin.data(), bin.size());
3,036✔
1416
            auto tmp = m_buffer.get();
3,036✔
1417
            if (m_value.is_type(type_String)) {
3,036✔
1418
                m_value = Mixed(StringData(tmp.data(), tmp.size()));
2,508✔
1419
            }
2,508✔
1420
            else {
528✔
1421
                m_value = Mixed(tmp);
528✔
1422
            }
528✔
1423
        }
3,036✔
1424
    }
13,146✔
1425

1426
    QueryValue m_value;
1427
    OwnedBinaryData m_buffer;
1428
    std::optional<ArrayMixed> m_leaf;
1429
    bool m_value_is_null = false;
1430
};
1431

1432
template <class TConditionFunction>
1433
class MixedNode : public MixedNodeBase {
1434
public:
1435
    using MixedNodeBase::MixedNodeBase;
1436

1437
    size_t find_first_local(size_t start, size_t end) override
1438
    {
21,300✔
1439
        TConditionFunction cond;
21,300✔
1440
        for (size_t i = start; i < end; i++) {
80,484✔
1441
            QueryValue val(m_leaf->get(i));
69,600✔
1442
            if constexpr (realm::is_any_v<TConditionFunction, BeginsWith, BeginsWithIns, EndsWith, EndsWithIns, Like,
34,800✔
1443
                                          LikeIns, NotEqualIns, Contains, ContainsIns>) {
55,800✔
1444
                // For some strange reason the parameters are swapped for string conditions
1445
                if (cond(m_value, val))
42,000✔
1446
                    return i;
7,704✔
1447
            }
21,000✔
1448
            else {
27,600✔
1449
                if (cond(val, m_value))
27,600✔
1450
                    return i;
13,128✔
1451
            }
27,600✔
1452
        }
69,600✔
1453
        return realm::npos;
10,884✔
1454
    }
21,300✔
1455

1456
    std::string describe_condition() const override
1457
    {
168✔
1458
        return TConditionFunction::description();
168✔
1459
    }
168✔
1460

1461
    std::unique_ptr<ParentNode> clone() const override
1462
    {
1,056✔
1463
        return std::unique_ptr<ParentNode>(new MixedNode(*this));
1,056✔
1464
    }
1,056✔
1465
};
1466

1467
template <>
1468
class MixedNode<Equal> : public MixedNodeBase {
1469
public:
1470
    MixedNode(Mixed v, ColKey column)
1471
        : MixedNodeBase(v, column)
2,787✔
1472
    {
5,574✔
1473
    }
5,574✔
1474
    MixedNode(const MixedNode<Equal>& other)
1475
        : MixedNodeBase(other)
2,796✔
1476
        , m_index_evaluator(other.m_index_evaluator)
2,796✔
1477
    {
5,592✔
1478
    }
5,592✔
1479
    void init(bool will_query_ranges) override;
1480

1481
    void cluster_changed() override
1482
    {
5,178✔
1483
        // If we use searchindex, we do not need further access to clusters
1484
        if (!has_search_index()) {
5,178✔
1485
            MixedNodeBase::cluster_changed();
2,688✔
1486
        }
2,688✔
1487
    }
5,178✔
1488

1489
    void table_changed() override
1490
    {
5,574✔
1491
        const bool has_index =
5,574✔
1492
            m_table.unchecked_ptr()->search_index_type(m_condition_column_key) == IndexType::General;
5,574✔
1493
        m_index_evaluator = has_index ? std::make_optional(IndexEvaluator{}) : std::nullopt;
5,574✔
1494
    }
5,574✔
1495

1496
    bool has_search_index() const override
1497
    {
10,026✔
1498
        return bool(m_index_evaluator);
10,026✔
1499
    }
10,026✔
1500

1501
    size_t find_first_local(size_t start, size_t end) override;
1502

1503
    std::string describe_condition() const override
1504
    {
60✔
1505
        return Equal::description();
60✔
1506
    }
60✔
1507

1508
    std::unique_ptr<ParentNode> clone() const override
1509
    {
5,592✔
1510
        return std::unique_ptr<ParentNode>(new MixedNode<Equal>(*this));
5,592✔
1511
    }
5,592✔
1512

1513
protected:
1514
    std::optional<IndexEvaluator> m_index_evaluator;
1515

1516
    const IndexEvaluator* index_based_keys() override
1517
    {
624✔
1518
        return m_index_evaluator ? &(*m_index_evaluator) : nullptr;
624✔
1519
    }
624✔
1520
};
1521

1522
template <>
1523
class MixedNode<EqualIns> : public MixedNodeBase {
1524
public:
1525
    MixedNode(Mixed v, ColKey column)
1526
        : MixedNodeBase(v, column)
48✔
1527
    {
96✔
1528
    }
96✔
1529
    MixedNode(const MixedNode<EqualIns>& other)
1530
        : MixedNodeBase(other)
66✔
1531
        , m_index_evaluator(other.m_index_evaluator)
66✔
1532
    {
132✔
1533
    }
132✔
1534
    void init(bool will_query_ranges) override;
1535

1536
    size_t find_first_local(size_t start, size_t end) override;
1537

1538
    void cluster_changed() override
1539
    {
66✔
1540
        // If we use searchindex, we do not need further access to clusters
1541
        if (!has_search_index()) {
66✔
1542
            MixedNodeBase::cluster_changed();
66✔
1543
        }
66✔
1544
    }
66✔
1545

1546
    void table_changed() override
1547
    {
96✔
1548
        const bool has_index =
96✔
1549
            m_table.unchecked_ptr()->search_index_type(m_condition_column_key) == IndexType::General;
96✔
1550
        m_index_evaluator = has_index ? std::make_optional(IndexEvaluator{}) : std::nullopt;
96✔
1551
    }
96✔
1552

1553
    bool has_search_index() const override
1554
    {
66✔
1555
        return bool(m_index_evaluator);
66✔
1556
    }
66✔
1557

1558
    std::string describe_condition() const override
1559
    {
18✔
1560
        return EqualIns::description();
18✔
1561
    }
18✔
1562

1563
    std::unique_ptr<ParentNode> clone() const override
1564
    {
132✔
1565
        return std::unique_ptr<ParentNode>(new MixedNode<EqualIns>(*this));
132✔
1566
    }
132✔
1567

1568
protected:
1569
    std::string m_ucase;
1570
    std::string m_lcase;
1571
    std::optional<IndexEvaluator> m_index_evaluator;
1572
    std::vector<ObjKey> m_index_matches;
1573

1574
    const IndexEvaluator* index_based_keys() override
1575
    {
96✔
1576
        return m_index_evaluator ? &(*m_index_evaluator) : nullptr;
96✔
1577
    }
96✔
1578
};
1579

1580
class StringNodeBase : public ParentNode {
1581
public:
1582
    using TConditionValue = StringData;
1583
    static const bool special_null_node = true;
1584

1585
    StringNodeBase(StringData v, ColKey column)
1586
        : m_value(v.is_null() ? util::none : util::make_optional(std::string(v)))
24,843✔
1587
        , m_string_value(m_value)
24,843✔
1588
    {
50,112✔
1589
        m_condition_column_key = column;
50,112✔
1590
        m_dT = 10.0;
50,112✔
1591
    }
50,112✔
1592

1593
    void table_changed() override
1594
    {
53,664✔
1595
        m_is_string_enum = m_table.unchecked_ptr()->is_enumerated(m_condition_column_key);
53,664✔
1596
    }
53,664✔
1597

1598
    void cluster_changed() override
1599
    {
111,432✔
1600
        m_leaf.emplace(m_table.unchecked_ptr()->get_alloc());
111,432✔
1601
        m_cluster->init_leaf(m_condition_column_key, &*m_leaf);
111,432✔
1602
    }
111,432✔
1603

1604
    void init(bool will_query_ranges) override
1605
    {
82,737✔
1606
        ParentNode::init(will_query_ranges);
82,737✔
1607

1608
        m_probes = 0;
82,737✔
1609
        m_matches = 0;
82,737✔
1610
        m_end_s = 0;
82,737✔
1611
        m_leaf_start = 0;
82,737✔
1612
        m_leaf_end = 0;
82,737✔
1613
    }
82,737✔
1614

1615
    virtual void clear_leaf_state()
1616
    {
16,782✔
1617
        m_leaf.reset();
16,782✔
1618
    }
16,782✔
1619

1620
    StringNodeBase(const StringNodeBase& from)
1621
        : ParentNode(from)
28,086✔
1622
        , m_value(from.m_value)
28,086✔
1623
        , m_string_value(m_value)
28,086✔
1624
        , m_is_string_enum(from.m_is_string_enum)
28,086✔
1625
    {
56,601✔
1626
    }
56,601✔
1627

1628
    std::string describe(util::serializer::SerialisationState& state) const override
1629
    {
6,843✔
1630
        REALM_ASSERT(m_condition_column_key);
6,843✔
1631
        return state.describe_column(ParentNode::m_table, m_condition_column_key) + " " + describe_condition() + " " +
6,843✔
1632
               util::serializer::print_value(m_string_value);
6,843✔
1633
    }
6,843✔
1634

1635
protected:
1636
    std::optional<std::string> m_value;
1637
    std::optional<ArrayString> m_leaf;
1638
    StringData m_string_value;
1639

1640
    bool m_is_string_enum = false;
1641

1642
    size_t m_end_s = 0;
1643
    size_t m_leaf_start = 0;
1644
    size_t m_leaf_end = 0;
1645

1646
    StringData get_string(size_t s)
1647
    {
408,564✔
1648
        return m_leaf->get(s);
408,564✔
1649
    }
408,564✔
1650
};
1651

1652
// Conditions for strings. Note that Equal is specialized later in this file!
1653
template <class TConditionFunction>
1654
class StringNode : public StringNodeBase {
1655
public:
1656
    constexpr static bool case_sensitive_comparison =
1657
        is_any_v<TConditionFunction, Greater, GreaterEqual, Less, LessEqual>;
1658
    StringNode(StringData v, ColKey column)
1659
        : StringNodeBase(v, column)
4,728✔
1660
    {
9,456✔
1661
        if constexpr (case_sensitive_comparison) {
9,456✔
1662
            return;
276✔
1663
        }
276✔
1664
        auto upper = case_map(v, true);
×
1665
        auto lower = case_map(v, false);
4,728✔
1666
        if (!upper || !lower) {
9,318✔
1667
            throw InvalidArgument(util::format("Malformed UTF-8: %1", v));
×
1668
        }
×
1669
        else {
9,318✔
1670
            m_ucase = std::move(*upper);
9,318✔
1671
            m_lcase = std::move(*lower);
9,318✔
1672
        }
9,318✔
1673
    }
4,728✔
1674

1675
    void init(bool will_query_ranges) override
1676
    {
13,602✔
1677
        StringNodeBase::init(will_query_ranges);
13,602✔
1678
        clear_leaf_state();
13,602✔
1679
    }
13,602✔
1680

1681
    size_t find_first_local(size_t start, size_t end) override
1682
    {
236,214✔
1683
        TConditionFunction cond;
236,214✔
1684

1685
        for (size_t s = start; s < end; ++s) {
445,428✔
1686
            StringData t = get_string(s);
318,036✔
1687

1688
            if constexpr (case_sensitive_comparison) {
318,036✔
1689
                // case insensitive not implemented for: >, >=, <, <=
1690
                if (cond(t, m_string_value))
154,755✔
1691
                    return s;
720✔
1692
            }
690✔
1693
            else {
316,656✔
1694
                if (cond(m_string_value, m_ucase.c_str(), m_lcase.c_str(), t))
316,656✔
1695
                    return s;
223,062✔
1696
            }
316,656✔
1697
        }
318,036✔
1698
        return not_found;
127,392✔
1699
    }
236,214✔
1700

1701
    std::string describe_condition() const override
1702
    {
264✔
1703
        return TConditionFunction::description();
264✔
1704
    }
264✔
1705

1706
    std::unique_ptr<ParentNode> clone() const override
1707
    {
8,808✔
1708
        return std::unique_ptr<ParentNode>(new StringNode<TConditionFunction>(*this));
8,808✔
1709
    }
8,808✔
1710

1711
    StringNode(const StringNode& from)
1712
        : StringNodeBase(from)
4,404✔
1713
        , m_ucase(from.m_ucase)
4,404✔
1714
        , m_lcase(from.m_lcase)
4,404✔
1715
    {
8,808✔
1716
    }
8,808✔
1717

1718
protected:
1719
    std::string m_ucase;
1720
    std::string m_lcase;
1721
};
1722

1723
// Specialization for Contains condition on Strings - we specialize because we can utilize Boyer-Moore
1724
template <>
1725
class StringNode<Contains> : public StringNodeBase {
1726
public:
1727
    StringNode(StringData v, ColKey column)
1728
        : StringNodeBase(v, column)
603✔
1729
        , m_charmap()
603✔
1730
    {
1,206✔
1731
        if (v.size() == 0)
1,206✔
1732
            return;
1,140✔
1733

1734
        // Build a dictionary of char-to-last distances in the search string
1735
        // (zero indicates that the char is not in needle)
1736
        size_t last_char_pos = v.size() - 1;
66✔
1737
        for (size_t i = 0; i < last_char_pos; ++i) {
3,090✔
1738
            // we never jump longer increments than 255 chars, even if needle is longer (to fit in one byte)
1739
            uint8_t jump = last_char_pos - i < 255 ? static_cast<uint8_t>(last_char_pos - i) : 255;
3,024✔
1740

1741
            unsigned char c = v[i];
3,024✔
1742
            m_charmap[c] = jump;
3,024✔
1743
        }
3,024✔
1744
        m_dT = 50.0;
66✔
1745
    }
66✔
1746

1747
    void init(bool will_query_ranges) override
1748
    {
1,746✔
1749
        StringNodeBase::init(will_query_ranges);
1,746✔
1750
        clear_leaf_state();
1,746✔
1751
    }
1,746✔
1752

1753

1754
    size_t find_first_local(size_t start, size_t end) override
1755
    {
33,174✔
1756
        Contains cond;
33,174✔
1757

1758
        for (size_t s = start; s < end; ++s) {
33,498✔
1759
            StringData t = get_string(s);
33,384✔
1760

1761
            if (cond(m_string_value, m_charmap, t))
33,384✔
1762
                return s;
33,060✔
1763
        }
33,384✔
1764
        return not_found;
114✔
1765
    }
33,174✔
1766

1767
    std::string describe_condition() const override
1768
    {
24✔
1769
        return Contains::description();
24✔
1770
    }
24✔
1771

1772

1773
    std::unique_ptr<ParentNode> clone() const override
1774
    {
1,140✔
1775
        return std::unique_ptr<ParentNode>(new StringNode<Contains>(*this));
1,140✔
1776
    }
1,140✔
1777

1778
    StringNode(const StringNode& from)
1779
        : StringNodeBase(from)
570✔
1780
        , m_charmap(from.m_charmap)
570✔
1781
    {
1,140✔
1782
    }
1,140✔
1783

1784
protected:
1785
    std::array<uint8_t, 256> m_charmap;
1786
};
1787

1788
// Specialization for ContainsIns condition on Strings - we specialize because we can utilize Boyer-Moore
1789
template <>
1790
class StringNode<ContainsIns> : public StringNodeBase {
1791
public:
1792
    StringNode(StringData v, ColKey column)
1793
        : StringNodeBase(v, column)
510✔
1794
        , m_charmap()
510✔
1795
    {
1,020✔
1796
        auto upper = case_map(v, true);
1,020✔
1797
        auto lower = case_map(v, false);
1,020✔
1798
        if (!upper || !lower) {
1,020✔
1799
            throw query_parser::InvalidQueryError(util::format("Malformed UTF-8: %1", v));
×
1800
        }
×
1801
        else {
1,020✔
1802
            m_ucase = std::move(*upper);
1,020✔
1803
            m_lcase = std::move(*lower);
1,020✔
1804
        }
1,020✔
1805

1806
        if (v.size() == 0)
1,020✔
1807
            return;
864✔
1808

1809
        // Build a dictionary of char-to-last distances in the search string
1810
        // (zero indicates that the char is not in needle)
1811
        size_t last_char_pos = m_ucase.size() - 1;
156✔
1812
        for (size_t i = 0; i < last_char_pos; ++i) {
3,474✔
1813
            // we never jump longer increments than 255 chars, even if needle is longer (to fit in one byte)
1814
            uint8_t jump = last_char_pos - i < 255 ? static_cast<uint8_t>(last_char_pos - i) : 255;
3,318✔
1815

1816
            unsigned char uc = m_ucase[i];
3,318✔
1817
            unsigned char lc = m_lcase[i];
3,318✔
1818
            m_charmap[uc] = jump;
3,318✔
1819
            m_charmap[lc] = jump;
3,318✔
1820
        }
3,318✔
1821
        m_dT = 75.0;
156✔
1822
    }
156✔
1823

1824
    void init(bool will_query_ranges) override
1825
    {
1,434✔
1826
        StringNodeBase::init(will_query_ranges);
1,434✔
1827
        clear_leaf_state();
1,434✔
1828
    }
1,434✔
1829

1830

1831
    size_t find_first_local(size_t start, size_t end) override
1832
    {
25,992✔
1833
        ContainsIns cond;
25,992✔
1834

1835
        for (size_t s = start; s < end; ++s) {
32,616✔
1836
            StringData t = get_string(s);
32,208✔
1837
            // The current behaviour is to return all results when querying for a null string.
1838
            // See comment above Query_NextGen_StringConditions on why every string including "" contains null.
1839
            if (!bool(m_value)) {
32,208✔
1840
                return s;
24,738✔
1841
            }
24,738✔
1842
            if (cond(m_string_value, m_ucase.c_str(), m_lcase.c_str(), m_charmap, t))
7,470✔
1843
                return s;
846✔
1844
        }
7,470✔
1845
        return not_found;
408✔
1846
    }
25,992✔
1847

1848
    std::string describe_condition() const override
1849
    {
66✔
1850
        return ContainsIns::description();
66✔
1851
    }
66✔
1852

1853
    std::unique_ptr<ParentNode> clone() const override
1854
    {
1,188✔
1855
        return std::unique_ptr<ParentNode>(new StringNode<ContainsIns>(*this));
1,188✔
1856
    }
1,188✔
1857

1858
    StringNode(const StringNode& from)
1859
        : StringNodeBase(from)
594✔
1860
        , m_charmap(from.m_charmap)
594✔
1861
        , m_ucase(from.m_ucase)
594✔
1862
        , m_lcase(from.m_lcase)
594✔
1863
    {
1,188✔
1864
    }
1,188✔
1865

1866
protected:
1867
    std::array<uint8_t, 256> m_charmap;
1868
    std::string m_ucase;
1869
    std::string m_lcase;
1870
};
1871

1872
class StringNodeEqualBase : public StringNodeBase {
1873
public:
1874
    StringNodeEqualBase(StringData v, ColKey column)
1875
        : StringNodeBase(v, column)
19,002✔
1876
    {
38,430✔
1877
    }
38,430✔
1878
    StringNodeEqualBase(const StringNodeEqualBase& from)
1879
        : StringNodeBase(from)
22,518✔
1880
        , m_index_evaluator(from.m_index_evaluator)
22,518✔
1881
    {
45,465✔
1882
    }
45,465✔
1883

1884
    void init(bool) override;
1885

1886
    bool has_search_index() const override
1887
    {
65,898✔
1888
        return bool(m_table.unchecked_ptr()->search_index_type(m_condition_column_key) == IndexType::General);
65,898✔
1889
    }
65,898✔
1890

1891
    void cluster_changed() override
1892
    {
304,650✔
1893
        // If we use searchindex, we do not need further access to clusters
1894
        if (!m_index_evaluator) {
304,650✔
1895
            StringNodeBase::cluster_changed();
92,628✔
1896
        }
92,628✔
1897
    }
304,650✔
1898

1899
    size_t find_first_local(size_t start, size_t end) override;
1900

1901
    std::string describe_condition() const override
1902
    {
6,477✔
1903
        return Equal::description();
6,477✔
1904
    }
6,477✔
1905

1906
    const IndexEvaluator* index_based_keys() override
1907
    {
43,437✔
1908
        return m_index_evaluator ? &(*m_index_evaluator) : nullptr;
43,437✔
1909
    }
43,437✔
1910

1911
protected:
1912
    std::optional<IndexEvaluator> m_index_evaluator;
1913

1914
    inline BinaryData str_to_bin(const StringData& s) noexcept
1915
    {
×
1916
        return BinaryData(s.data(), s.size());
×
1917
    }
×
1918

1919
    virtual void _search_index_init() = 0;
1920
    virtual size_t _find_first_local(size_t start, size_t end) = 0;
1921
};
1922

1923
// Specialization for Equal condition on Strings - we specialize because we can utilize indexes (if they exist) for
1924
// Equal. This specialisation also supports combining other StringNode<Equal> conditions into itself in order to
1925
// optimise the non-indexed linear search that can be happen when many conditions are OR'd together in an "IN" query.
1926
// Future optimization: make specialization for greater, notequal, etc
1927
template <>
1928
class StringNode<Equal> : public StringNodeEqualBase {
1929
public:
1930
    StringNode(StringData v, ColKey column)
1931
        : StringNodeEqualBase(v, column)
18,195✔
1932
    {
36,828✔
1933
    }
36,828✔
1934
    StringNode(ColKey col, const Mixed* begin, const Mixed* end);
1935

1936
    void _search_index_init() override;
1937

1938
    bool do_consume_condition(ParentNode& other) override;
1939

1940
    std::unique_ptr<ParentNode> clone() const override
1941
    {
44,067✔
1942
        return std::unique_ptr<ParentNode>(new StringNode<Equal>(*this));
44,067✔
1943
    }
44,067✔
1944

1945
    std::string describe(util::serializer::SerialisationState& state) const override;
1946

1947
    StringNode(const StringNode& from)
1948
        : StringNodeEqualBase(from)
21,819✔
1949
    {
44,067✔
1950
        for (auto& needle : from.m_needles) {
44,067✔
1951
            if (needle.is_null()) {
744✔
1952
                m_needles.emplace();
102✔
1953
            }
102✔
1954
            else {
642✔
1955
                m_needle_storage.push_back(std::make_unique<char[]>(needle.size()));
642✔
1956
                std::copy(needle.data(), needle.data() + needle.size(), m_needle_storage.back().get());
642✔
1957
                m_needles.insert(StringData(m_needle_storage.back().get(), needle.size()));
642✔
1958
            }
642✔
1959
        }
744✔
1960
    }
44,067✔
1961

1962
private:
1963
    size_t _find_first_local(size_t start, size_t end) override;
1964
    std::unordered_set<StringData> m_needles;
1965
    std::vector<std::unique_ptr<char[]>> m_needle_storage;
1966
};
1967

1968

1969
// Specialization for EqualIns condition on Strings - we specialize because we can utilize indexes (if they exist) for
1970
// EqualIns.
1971
template <>
1972
class StringNode<EqualIns> : public StringNodeEqualBase {
1973
public:
1974
    StringNode(StringData v, ColKey column)
1975
        : StringNodeEqualBase(v, column)
480✔
1976
    {
960✔
1977
        auto upper = case_map(v, true);
960✔
1978
        auto lower = case_map(v, false);
960✔
1979
        if (!upper || !lower) {
960✔
1980
            throw query_parser::InvalidQueryError(util::format("Malformed UTF-8: %1", v));
×
1981
        }
×
1982
        else {
960✔
1983
            m_ucase = std::move(*upper);
960✔
1984
            m_lcase = std::move(*lower);
960✔
1985
        }
960✔
1986
    }
960✔
1987

1988
    void _search_index_init() override;
1989

1990
    std::string describe_condition() const override
1991
    {
12✔
1992
        return EqualIns::description();
12✔
1993
    }
12✔
1994

1995
    std::unique_ptr<ParentNode> clone() const override
1996
    {
930✔
1997
        return std::unique_ptr<ParentNode>(new StringNode(*this));
930✔
1998
    }
930✔
1999

2000
    StringNode(const StringNode& from)
2001
        : StringNodeEqualBase(from)
465✔
2002
        , m_ucase(from.m_ucase)
465✔
2003
        , m_lcase(from.m_lcase)
465✔
2004
    {
930✔
2005
    }
930✔
2006

2007
private:
2008
    std::vector<ObjKey> m_index_matches;
2009
    std::string m_ucase;
2010
    std::string m_lcase;
2011
    std::vector<ObjKey> storage;
2012
    size_t _find_first_local(size_t start, size_t end) override;
2013
};
2014

2015

2016
class StringNodeFulltext : public StringNodeEqualBase {
2017
public:
2018
    StringNodeFulltext(StringData v, ColKey column, std::unique_ptr<LinkMap> lm = {});
2019

2020
    void table_changed() override;
2021

2022
    void _search_index_init() override;
2023

2024
    bool has_search_index() const override
2025
    {
378✔
2026
        return true; // it's a required precondition for fulltext queries
378✔
2027
    }
378✔
2028

2029
    std::unique_ptr<ParentNode> clone() const override
2030
    {
468✔
2031
        return std::unique_ptr<ParentNode>(new StringNodeFulltext(*this));
468✔
2032
    }
468✔
2033

2034
    std::string describe_condition() const override
2035
    {
×
2036
        return "FULLTEXT";
×
2037
    }
×
2038

2039
private:
2040
    std::vector<ObjKey> m_index_matches;
2041
    std::unique_ptr<LinkMap> m_link_map;
2042
    StringNodeFulltext(const StringNodeFulltext&);
2043

2044
    size_t _find_first_local(size_t, size_t) override
2045
    {
×
2046
        REALM_UNREACHABLE();
2047
    }
×
2048
};
2049

2050
// OR node contains at least two node pointers: Two or more conditions to OR
2051
// together in m_conditions, and the next AND condition (if any) in m_child.
2052
//
2053
// For 'second.equal(23).begin_group().first.equal(111).Or().first.equal(222).end_group().third().equal(555)', this
2054
// will first set m_conditions[0] = left-hand-side through constructor, and then later, when .first.equal(222) is
2055
// invoked, invocation will set m_conditions[1] = right-hand-side through Query& Query::Or() (see query.cpp).
2056
// In there, m_child is also set to next AND condition (if any exists) following the OR.
2057
class OrNode : public ParentNode {
2058
public:
2059
    OrNode(std::unique_ptr<ParentNode> condition)
2060
    {
26,985✔
2061
        m_dT = 50.0;
26,985✔
2062
        if (condition)
26,985✔
2063
            m_conditions.emplace_back(std::move(condition));
26,085✔
2064
    }
26,985✔
2065

2066
    OrNode(const OrNode& other)
2067
        : ParentNode(other)
14,148✔
2068
    {
27,999✔
2069
        for (const auto& condition : other.m_conditions) {
731,394✔
2070
            m_conditions.emplace_back(condition->clone());
731,394✔
2071
        }
731,394✔
2072
    }
27,999✔
2073

2074
    void table_changed() override
2075
    {
27,033✔
2076
        for (auto& condition : m_conditions) {
27,033✔
2077
            condition->set_table(m_table);
26,181✔
2078
        }
26,181✔
2079
    }
27,033✔
2080

2081
    void cluster_changed() override
2082
    {
56,934✔
2083
        for (auto& condition : m_conditions) {
327,672✔
2084
            condition->set_cluster(m_cluster);
327,672✔
2085
        }
327,672✔
2086

2087
        m_start.clear();
56,934✔
2088
        m_start.resize(m_conditions.size(), 0);
56,934✔
2089

2090
        m_last.clear();
56,934✔
2091
        m_last.resize(m_conditions.size(), 0);
56,934✔
2092

2093
        m_was_match.clear();
56,934✔
2094
        m_was_match.resize(m_conditions.size(), false);
56,934✔
2095
    }
56,934✔
2096

2097
    std::string describe(util::serializer::SerialisationState& state) const override
2098
    {
594✔
2099
        std::string s;
594✔
2100
        for (size_t i = 0; i < m_conditions.size(); ++i) {
3,006✔
2101
            if (m_conditions[i]) {
2,412✔
2102
                s += m_conditions[i]->describe_expression(state);
2,412✔
2103
                if (i != m_conditions.size() - 1) {
2,412✔
2104
                    s += " or ";
1,818✔
2105
                }
1,818✔
2106
            }
2,412✔
2107
        }
2,412✔
2108
        if (m_conditions.size() > 1) {
594✔
2109
            s = "(" + s + ")";
528✔
2110
        }
528✔
2111
        return s;
594✔
2112
    }
594✔
2113

2114
    void collect_dependencies(std::vector<TableKey>& versions) const override
2115
    {
24,993✔
2116
        for (const auto& cond : m_conditions) {
25,647✔
2117
            cond->collect_dependencies(versions);
25,647✔
2118
        }
25,647✔
2119
    }
24,993✔
2120

2121
    void init(bool will_query_ranges) override
2122
    {
27,072✔
2123
        ParentNode::init(will_query_ranges);
27,072✔
2124
        combine_conditions(!will_query_ranges);
27,072✔
2125

2126
        m_start.clear();
27,072✔
2127
        m_start.resize(m_conditions.size(), 0);
27,072✔
2128

2129
        m_last.clear();
27,072✔
2130
        m_last.resize(m_conditions.size(), 0);
27,072✔
2131

2132
        m_was_match.clear();
27,072✔
2133
        m_was_match.resize(m_conditions.size(), false);
27,072✔
2134

2135
        std::vector<ParentNode*> v;
27,072✔
2136
        for (auto& condition : m_conditions) {
276,774✔
2137
            condition->init(will_query_ranges);
276,774✔
2138
            v.clear();
276,774✔
2139
            condition->gather_children(v);
276,774✔
2140
        }
276,774✔
2141
    }
27,072✔
2142

2143
    size_t find_first_local(size_t start, size_t end) override
2144
    {
3,097,197✔
2145
        if (start >= end)
3,097,197✔
2146
            return not_found;
885✔
2147

2148
        size_t index = not_found;
3,096,312✔
2149

2150
        for (size_t c = 0; c < m_conditions.size(); ++c) {
14,759,298✔
2151
            // out of order search; have to discard cached results
2152
            if (start < m_start[c]) {
11,662,986✔
2153
                m_last[c] = 0;
×
2154
                m_was_match[c] = false;
×
2155
            }
×
2156
            // already searched this range and didn't match
2157
            else if (m_last[c] >= end)
11,662,986✔
2158
                continue;
3,490,989✔
2159
            // already search this range and *did* match
2160
            else if (m_was_match[c] && m_last[c] >= start) {
8,171,997✔
2161
                if (index > m_last[c])
4,484,655✔
2162
                    index = m_last[c];
962,106✔
2163
                continue;
4,484,655✔
2164
            }
4,484,655✔
2165

2166
            m_start[c] = start;
3,687,342✔
2167
            size_t fmax = std::max(m_last[c], start);
3,687,342✔
2168
            size_t f = m_conditions[c]->find_first(fmax, end);
3,687,342✔
2169
            m_was_match[c] = f != not_found;
3,687,342✔
2170
            m_last[c] = f == not_found ? end : f;
3,687,342✔
2171
            if (f != not_found && index > m_last[c])
3,687,342✔
2172
                index = m_last[c];
2,581,443✔
2173
        }
3,687,342✔
2174

2175
        return index;
3,096,312✔
2176
    }
3,097,197✔
2177

2178
    std::string validate() override
2179
    {
18✔
2180
        if (m_conditions.size() == 0)
18✔
2181
            return "Missing both arguments of OR";
6✔
2182
        if (m_conditions.size() == 1)
12✔
2183
            return "Missing argument of OR";
12✔
2184
        std::string s;
×
2185
        if (m_child != 0)
×
2186
            s = m_child->validate();
×
2187
        if (s != "")
×
2188
            return s;
×
2189
        for (size_t i = 0; i < m_conditions.size(); ++i) {
×
2190
            s = m_conditions[i]->validate();
×
2191
            if (s != "")
×
2192
                return s;
×
2193
        }
×
2194
        return "";
×
2195
    }
×
2196

2197
    std::unique_ptr<ParentNode> clone() const override
2198
    {
27,999✔
2199
        return std::unique_ptr<ParentNode>(new OrNode(*this));
27,999✔
2200
    }
27,999✔
2201

2202
    std::vector<std::unique_ptr<ParentNode>> m_conditions;
2203

2204
private:
2205
    struct ConditionType {
2206
        ConditionType(const ParentNode& node)
2207
            : m_col(node.m_condition_column_key.value)
9,018,618✔
2208
            , m_type(typeid(node))
9,018,618✔
2209
        {
10,426,413✔
2210
        }
10,426,413✔
2211
        int64_t m_col;
2212
        std::type_index m_type;
2213
        bool operator<(const ConditionType& other) const
2214
        {
5,559,678✔
2215
            return this->m_col < other.m_col && this->m_type < other.m_type;
5,559,678✔
2216
        }
5,559,678✔
2217
        bool operator!=(const ConditionType& other) const
2218
        {
247,047✔
2219
            return this->m_col != other.m_col || this->m_type != other.m_type;
247,047✔
2220
        }
247,047✔
2221
    };
2222

2223
    void combine_conditions(bool ignore_indexes)
2224
    {
27,075✔
2225
        // Although ColKey is not unique per table, it is not important to consider
2226
        // the table when sorting here because ParentNode::m_condition_column_key
2227
        // is only a valid ColKey when the node has a direct condition on a column
2228
        // of the table this query is running on. Any link query nodes use a special
2229
        // LinkChain state to store the column path.
2230
        std::sort(m_conditions.begin(), m_conditions.end(), [](auto& a, auto& b) {
5,088,873✔
2231
            return ConditionType(*a) < ConditionType(*b);
5,088,873✔
2232
        });
5,088,873✔
2233

2234
        bool compute_condition_counts = num_conditions_may_need_combination_counts(m_conditions.size());
27,075✔
2235
        util::FlatMap<ConditionType, size_t> condition_type_counts;
27,075✔
2236
        if (compute_condition_counts) {
27,075✔
2237
            for (auto it = m_conditions.begin(); it != m_conditions.end();) {
384✔
2238
                // no need to try to combine anything other than simple nodes that
2239
                // filter directly on a top-level column since the only nodes that
2240
                // support combinations are string/int/uuid/oid <Equal> types
2241
                if (!(*it)->m_condition_column_key) {
192✔
2242
                    ++it;
×
2243
                    continue;
×
2244
                }
×
2245
                ConditionType cur_type(*(*it));
192✔
2246
                auto next = std::upper_bound(it, m_conditions.end(), cur_type,
192✔
2247
                                             [](const ConditionType& a, const std::unique_ptr<ParentNode>& b) {
1,434✔
2248
                                                 return a < ConditionType(*b);
1,434✔
2249
                                             });
1,434✔
2250
                condition_type_counts[cur_type] = next - it;
192✔
2251
                it = next;
192✔
2252
            }
192✔
2253
        }
192✔
2254

2255
        ParentNode* prev = m_conditions.begin()->get();
27,075✔
2256
        std::optional<size_t> cur_type_count;
27,075✔
2257
        if (compute_condition_counts) {
27,075✔
2258
            cur_type_count = condition_type_counts[ConditionType{*prev}];
192✔
2259
        }
192✔
2260
        auto cond = [&](auto& node) {
708,432✔
2261
            if (prev->consume_condition(*node, ignore_indexes, cur_type_count))
708,432✔
2262
                return true;
458,736✔
2263
            prev = &*node;
249,696✔
2264
            if (compute_condition_counts) {
249,696✔
2265
                cur_type_count = condition_type_counts[ConditionType{*prev}];
246,855✔
2266
            }
246,855✔
2267
            return false;
249,696✔
2268
        };
708,432✔
2269
        m_conditions.erase(std::remove_if(m_conditions.begin() + 1, m_conditions.end(), cond), m_conditions.end());
27,075✔
2270
    }
27,075✔
2271

2272
    // start index of the last find for each cond
2273
    std::vector<size_t> m_start;
2274
    // last looked at index of the last find for each cond
2275
    // is a matching index if m_was_match is true
2276
    std::vector<size_t> m_last;
2277
    std::vector<bool> m_was_match;
2278
};
2279

2280

2281
class NotNode : public ParentNode {
2282
public:
2283
    NotNode(std::unique_ptr<ParentNode> condition)
2284
        : m_condition(std::move(condition))
1,101✔
2285
    {
2,202✔
2286
        m_dT = 50.0;
2,202✔
2287
        if (!m_condition) {
2,202✔
2288
            throw query_parser::InvalidQueryError("Missing argument to Not");
×
2289
        }
×
2290
    }
2,202✔
2291

2292
    void table_changed() override
2293
    {
2,244✔
2294
        m_condition->set_table(m_table);
2,244✔
2295
    }
2,244✔
2296

2297
    void cluster_changed() override
2298
    {
2,730✔
2299
        m_condition->set_cluster(m_cluster);
2,730✔
2300
        // Heuristics bookkeeping:
2301
        m_known_range_start = 0;
2,730✔
2302
        m_known_range_end = 0;
2,730✔
2303
        m_first_in_known_range = not_found;
2,730✔
2304
    }
2,730✔
2305

2306
    void init(bool will_query_ranges) override
2307
    {
2,280✔
2308
        ParentNode::init(will_query_ranges);
2,280✔
2309
        std::vector<ParentNode*> v;
2,280✔
2310

2311
        m_condition->init(false);
2,280✔
2312
        v.clear();
2,280✔
2313
        m_condition->gather_children(v);
2,280✔
2314
    }
2,280✔
2315

2316
    size_t find_first_local(size_t start, size_t end) override;
2317

2318
    std::string describe(util::serializer::SerialisationState& state) const override
2319
    {
924✔
2320
        if (m_condition) {
924✔
2321
            return "!(" + m_condition->describe_expression(state) + ")";
924✔
2322
        }
924✔
2323
        return "!()";
×
2324
    }
924✔
2325

2326
    void collect_dependencies(std::vector<TableKey>& versions) const override
2327
    {
48✔
2328
        if (m_condition) {
48✔
2329
            m_condition->collect_dependencies(versions);
48✔
2330
        }
48✔
2331
    }
48✔
2332

2333
    std::unique_ptr<ParentNode> clone() const override
2334
    {
4,080✔
2335
        return std::unique_ptr<ParentNode>(new NotNode(*this));
4,080✔
2336
    }
4,080✔
2337

2338
    NotNode(const NotNode& from)
2339
        : ParentNode(from)
2,040✔
2340
        , m_condition(from.m_condition ? from.m_condition->clone() : nullptr)
2,040✔
2341
        , m_known_range_start(from.m_known_range_start)
2,040✔
2342
        , m_known_range_end(from.m_known_range_end)
2,040✔
2343
        , m_first_in_known_range(from.m_first_in_known_range)
2,040✔
2344
    {
4,080✔
2345
    }
4,080✔
2346

2347
    std::unique_ptr<ParentNode> m_condition;
2348

2349
private:
2350
    // FIXME This heuristic might as well be reused for all condition nodes.
2351
    size_t m_known_range_start;
2352
    size_t m_known_range_end;
2353
    size_t m_first_in_known_range;
2354

2355
    bool evaluate_at(size_t rowndx);
2356
    void update_known(size_t start, size_t end, size_t first);
2357
    size_t find_first_loop(size_t start, size_t end);
2358
    size_t find_first_covers_known(size_t start, size_t end);
2359
    size_t find_first_covered_by_known(size_t start, size_t end);
2360
    size_t find_first_overlap_lower(size_t start, size_t end);
2361
    size_t find_first_overlap_upper(size_t start, size_t end);
2362
    size_t find_first_no_overlap(size_t start, size_t end);
2363
};
2364

2365
// Compare two columns with eachother row-by-row
2366
class TwoColumnsNodeBase : public ParentNode {
2367
public:
2368
    TwoColumnsNodeBase(ColKey column1, ColKey column2)
2369
    {
23,568✔
2370
        m_dT = 100.0;
23,568✔
2371
        m_condition_column_key1 = column1;
23,568✔
2372
        m_condition_column_key2 = column2;
23,568✔
2373
        if (m_condition_column_key1.is_collection() || m_condition_column_key2.is_collection()) {
23,568✔
2374
            throw Exception(ErrorCodes::InvalidQuery,
×
2375
                            util::format("queries comparing two properties are not yet supported for "
×
2376
                                         "collections (list/set/dictionary) (%1 and %2)",
×
2377
                                         ParentNode::m_table->get_column_name(m_condition_column_key1),
×
2378
                                         ParentNode::m_table->get_column_name(m_condition_column_key2)));
×
2379
        }
×
2380
    }
23,568✔
2381

2382
    void table_changed() override
2383
    {
26,808✔
2384
        if (m_table) {
26,808✔
2385
            ParentNode::m_table->check_column(m_condition_column_key1);
26,808✔
2386
            ParentNode::m_table->check_column(m_condition_column_key2);
26,808✔
2387
        }
26,808✔
2388
    }
26,808✔
2389

2390
    static std::unique_ptr<ArrayPayload> update_cached_leaf_pointers_for_column(Allocator& alloc,
2391
                                                                                const ColKey& col_key);
2392
    void cluster_changed() override
2393
    {
30,921✔
2394
        if (!m_leaf1) {
30,921✔
2395
            m_leaf1 =
29,124✔
2396
                update_cached_leaf_pointers_for_column(m_table.unchecked_ptr()->get_alloc(), m_condition_column_key1);
29,124✔
2397
        }
29,124✔
2398
        if (!m_leaf2) {
30,921✔
2399
            m_leaf2 =
29,124✔
2400
                update_cached_leaf_pointers_for_column(m_table.unchecked_ptr()->get_alloc(), m_condition_column_key2);
29,124✔
2401
        }
29,124✔
2402
        m_cluster->init_leaf(m_condition_column_key1, m_leaf1.get());
30,921✔
2403
        m_cluster->init_leaf(m_condition_column_key2, m_leaf2.get());
30,921✔
2404
    }
30,921✔
2405

2406
    std::string describe(util::serializer::SerialisationState& state) const override
2407
    {
×
2408
        REALM_ASSERT(m_condition_column_key1 && m_condition_column_key2);
×
2409
        return state.describe_column(ParentNode::m_table, m_condition_column_key1) + " " + describe_condition() +
×
2410
               " " + state.describe_column(ParentNode::m_table, m_condition_column_key2);
×
2411
    }
×
2412

2413
    TwoColumnsNodeBase(const TwoColumnsNodeBase& from)
2414
        : ParentNode(from)
10,914✔
2415
        , m_condition_column_key1(from.m_condition_column_key1)
10,914✔
2416
        , m_condition_column_key2(from.m_condition_column_key2)
10,914✔
2417
    {
21,828✔
2418
    }
21,828✔
2419

2420
protected:
2421
    ColKey m_condition_column_key1;
2422
    ColKey m_condition_column_key2;
2423
    std::unique_ptr<ArrayPayload> m_leaf1;
2424
    std::unique_ptr<ArrayPayload> m_leaf2;
2425
};
2426

2427

2428
template <class TConditionFunction>
2429
class TwoColumnsNode : public TwoColumnsNodeBase {
2430
public:
2431
    using TwoColumnsNodeBase::TwoColumnsNodeBase;
2432
    size_t find_first_local(size_t start, size_t end) override
2433
    {
291,729✔
2434
        size_t s = start;
291,729✔
2435
        while (s < end) {
766,365✔
2436
            QueryValue v1(m_leaf1->get_any(s));
726,123✔
2437
            QueryValue v2(m_leaf2->get_any(s));
726,123✔
2438
            if (TConditionFunction()(v1, v2))
726,123✔
2439
                return s;
251,487✔
2440
            else
474,636✔
2441
                s++;
474,636✔
2442
        }
726,123✔
2443
        return not_found;
40,242✔
2444
    }
291,729✔
2445

2446
    std::string describe_condition() const override
2447
    {
×
2448
        return TConditionFunction::description();
×
2449
    }
×
2450

2451
    std::unique_ptr<ParentNode> clone() const override
2452
    {
21,828✔
2453
        return std::unique_ptr<ParentNode>(new TwoColumnsNode<TConditionFunction>(*this));
21,828✔
2454
    }
21,828✔
2455
};
2456

2457

2458
// For Next-Generation expressions like col1 / col2 + 123 > col4 * 100.
2459
class ExpressionNode : public ParentNode {
2460
public:
2461
    ExpressionNode(std::unique_ptr<Expression>);
2462

2463
    void init(bool) override;
2464
    size_t find_first_local(size_t start, size_t end) override;
2465

2466
    void table_changed() override;
2467
    void cluster_changed() override;
2468
    void collect_dependencies(std::vector<TableKey>&) const override;
2469

2470
    std::string describe(util::serializer::SerialisationState& state) const override;
2471

2472
    std::unique_ptr<ParentNode> clone() const override;
2473

2474
private:
2475
    ExpressionNode(const ExpressionNode& from);
2476

2477
    std::unique_ptr<Expression> m_expression;
2478
};
2479

2480

2481
class LinksToNodeBase : public ParentNode {
2482
public:
2483
    LinksToNodeBase(ColKey origin_column_key, ObjKey target_key)
2484
        : LinksToNodeBase(origin_column_key, std::vector<ObjKey>{target_key})
6,810✔
2485
    {
13,620✔
2486
    }
13,620✔
2487

2488
    LinksToNodeBase(ColKey origin_column_key, const std::vector<ObjKey>& target_keys)
2489
        : m_target_keys(target_keys)
7,044✔
2490
    {
14,088✔
2491
        m_dT = 50.0;
14,088✔
2492
        m_condition_column_key = origin_column_key;
14,088✔
2493
        auto column_type = origin_column_key.get_type();
14,088✔
2494
        REALM_ASSERT(column_type == col_type_Link);
14,088✔
2495
        REALM_ASSERT(!m_target_keys.empty());
14,088✔
2496
    }
14,088✔
2497

2498
    void cluster_changed() override
2499
    {
50,928✔
2500
        if (m_condition_column_key.is_collection()) {
50,928✔
2501
            m_linklist.emplace(m_table.unchecked_ptr()->get_alloc());
24,924✔
2502
            m_leaf = &*m_linklist;
24,924✔
2503
        }
24,924✔
2504
        else {
26,004✔
2505
            m_list.emplace(m_table.unchecked_ptr()->get_alloc());
26,004✔
2506
            m_leaf = &*m_list;
26,004✔
2507
        }
26,004✔
2508
        m_cluster->init_leaf(this->m_condition_column_key, m_leaf);
50,928✔
2509
    }
50,928✔
2510

2511
    std::string describe(util::serializer::SerialisationState& state) const override
2512
    {
252✔
2513
        REALM_ASSERT(m_condition_column_key);
252✔
2514
        std::string links = m_target_keys.size() > 1 ? "{" : "";
252✔
2515
        Group* g = m_table->get_parent_group();
252✔
2516
        auto target_table_key = m_table->get_opposite_table(m_condition_column_key)->get_key();
252✔
2517
        int cnt = 0;
252✔
2518
        for (auto key : m_target_keys) {
264✔
2519
            if (cnt++) {
264✔
2520
                links += ",";
12✔
2521
            }
12✔
2522
            links += util::serializer::print_value(ObjLink(target_table_key, key), g);
264✔
2523
        }
264✔
2524
        if (m_target_keys.size() > 1) {
252✔
2525
            links += "}";
12✔
2526
        }
12✔
2527
        return state.describe_column(ParentNode::m_table, m_condition_column_key) + " " + describe_condition() + " " +
252✔
2528
               links;
252✔
2529
    }
252✔
2530

2531
protected:
2532
    std::vector<ObjKey> m_target_keys;
2533
    std::optional<ArrayKey> m_list;
2534
    std::optional<ArrayList> m_linklist;
2535
    ArrayPayload* m_leaf = nullptr;
2536

2537
    LinksToNodeBase(const LinksToNodeBase& source)
2538
        : ParentNode(source)
7,686✔
2539
        , m_target_keys(source.m_target_keys)
7,686✔
2540
    {
15,372✔
2541
    }
15,372✔
2542

2543
    ref_type get_ref(size_t i)
2544
    {
6,025,638✔
2545
        if (m_list)
6,025,638✔
2546
            return m_list->get_as_ref(i);
×
2547
        return m_linklist->get(i);
6,025,638✔
2548
    }
6,025,638✔
2549
};
2550

2551
template <class TConditionFunction>
2552
class LinksToNode : public LinksToNodeBase {
2553
public:
2554
    using LinksToNodeBase::LinksToNodeBase;
2555

2556
    std::string describe_condition() const override
2557
    {
252✔
2558
        return TConditionFunction::description();
252✔
2559
    }
252✔
2560

2561
    std::unique_ptr<ParentNode> clone() const override
2562
    {
15,372✔
2563
        return std::unique_ptr<ParentNode>(new LinksToNode<TConditionFunction>(*this));
15,372✔
2564
    }
15,372✔
2565

2566
    size_t find_first_local(size_t start, size_t end) override;
2567
};
2568

2569
} // namespace realm
2570

2571
#endif // REALM_QUERY_ENGINE_HPP
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

© 2026 Coveralls, Inc