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

realm / realm-core / 2278

30 Apr 2024 03:09PM UTC coverage: 90.747% (+0.01%) from 90.737%
2278

push

Evergreen

web-flow
Build with -Werror on CI (#7646)

We used to have the Jenkins error parser that made the job fail if there were
any warnings, but now we have nothing and warnings can slip through. This is a
little worse as it makes the build fail immediately rather than letting it run
the tests, but it's better than nothing.

101926 of 180232 branches covered (56.55%)

3 of 5 new or added lines in 4 files covered. (60.0%)

35 existing lines in 10 files now uncovered.

212472 of 234137 relevant lines covered (90.75%)

5729922.96 hits per line

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

94.13
/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,551,890✔
113
    virtual ~ParentNode() = default;
5,680,053✔
114

115
    virtual bool has_search_index() const
116
    {
29,580✔
117
        return false;
29,580✔
118
    }
29,580✔
119
    virtual const IndexEvaluator* index_based_keys()
120
    {
306,987✔
121
        return nullptr;
306,987✔
122
    }
306,987✔
123

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

130
        if (m_child)
2,245,761✔
131
            m_child->gather_children(v);
23,844✔
132

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

138
    double cost() const
139
    {
7,130,439✔
140
        constexpr size_t bitwidth_time_unit = 64;
7,130,439✔
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;
7,130,439✔
143
    }
7,130,439✔
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,190,303✔
151
        m_dD = 100.0;
2,190,303✔
152

153
        if (m_child)
2,190,303✔
154
            m_child->init(will_query_ranges);
23,844✔
155
    }
2,190,303✔
156

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

164
    void set_table(ConstTableRef table)
165
    {
3,310,011✔
166
        if (table == m_table)
3,310,011✔
167
            return;
734,247✔
168

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

175
    void set_cluster(const Cluster* cluster)
176
    {
8,187,480✔
177
        m_cluster = cluster;
8,187,480✔
178
        if (m_child)
8,187,480✔
179
            m_child->set_cluster(cluster);
121,239✔
180
        cluster_changed();
8,187,480✔
181
    }
8,187,480✔
182

183
    virtual void collect_dependencies(std::vector<TableKey>&) const {}
1,619,784✔
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
    {
23,574✔
200
        if (m_child)
23,574✔
201
            m_child->add_child(std::move(child));
648✔
202
        else
22,926✔
203
            m_child = std::move(child);
22,926✔
204
    }
23,574✔
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
    {
47,829✔
220
        std::string s;
47,829✔
221
        s = describe(state);
47,829✔
222
        if (m_child) {
47,829✔
223
            s = s + " and " + m_child->describe_expression(state);
936✔
224
        }
936✔
225
        return s;
47,829✔
226
    }
47,829✔
227

228
    bool consume_condition(ParentNode& other, bool ignore_indexes, std::optional<size_t> num_identical_conditions)
229
    {
717,573✔
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)
717,573✔
233
            return false;
3,174✔
234
        if (m_child || other.m_child)
714,399✔
235
            return false;
204✔
236
        if (typeid(*this) != typeid(other))
714,195✔
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 &&
714,165✔
253
            (m_condition_column_key.get_type() == col_type_String || !num_identical_conditions ||
714,165✔
254
             *num_identical_conditions < c_threshold_of_conditions_overwhelming_index))
642,222✔
255
            return false;
214,185✔
256
        return do_consume_condition(other);
499,980✔
257
    }
714,165✔
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
    {
26,781✔
262
        return num_total_conditions >= c_threshold_of_conditions_overwhelming_index;
26,781✔
263
    }
26,781✔
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() {}
1,737,135✔
288
    virtual void cluster_changed()
289
    {
×
290
        // TODO: Should eventually be pure
291
    }
×
292
    virtual bool do_consume_condition(ParentNode&)
293
    {
41,730✔
294
        return false;
41,730✔
295
    }
41,730✔
296
};
297

298

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

306
    ColumnNodeBase(const ColumnNodeBase& from)
307
        : ParentNode(from)
872,907✔
308
    {
1,727,394✔
309
    }
1,727,394✔
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,880✔
321
        if (m_matching_keys) {
14,880✔
322
            return m_matching_keys->size();
444✔
323
        }
444✔
324
        return m_results_end - m_results_start;
14,436✔
325
    }
14,880✔
326
    ObjKey get(size_t ndx) const
327
    {
265,932✔
328
        return get_internal(ndx + m_results_start);
265,932✔
329
    }
265,932✔
330

331
private:
332
    ObjKey get_internal(size_t ndx) const
333
    {
72,414,630✔
334
        if (m_matching_keys) {
72,414,630✔
335
            return m_matching_keys->at(ndx);
606✔
336
        }
606✔
337
        if (m_index_matches) {
72,414,024✔
338
            return ObjKey(m_index_matches->get(ndx));
72,404,739✔
339
        }
72,404,739✔
340
        else if (m_results_end == 1) {
9,285✔
341
            REALM_ASSERT_EX(ndx == 0, ndx);
9,285✔
342
            return m_actual_key;
9,285✔
343
        }
9,285✔
UNCOV
344
        return ObjKey();
×
345
    }
72,414,024✔
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)
835,551✔
365
        , m_value(std::move(value))
823,722✔
366
    {
1,656,150✔
367
    }
1,656,150✔
368

369
    IntegerNodeBase(const IntegerNodeBase& from)
370
        : ColumnNodeBase(from)
874,362✔
371
        , m_value(from.m_value)
847,245✔
372
    {
1,736,277✔
373
    }
1,736,277✔
374

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

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

385
        m_dT = .25;
1,670,712✔
386
    }
1,670,712✔
387

388
    template <class TConditionFunction>
389
    size_t find_all_local(size_t start, size_t end)
390
    {
6,109,494✔
391
        m_leaf->template find<TConditionFunction>(m_value, start, end, m_state);
6,109,494✔
392
        return end;
6,109,494✔
393
    }
6,109,494✔
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)
36,750✔
419
    {
78,003✔
420
    }
78,003✔
421
    IntegerNode(const IntegerNode& from)
422
        : BaseType(from)
58,968✔
423
    {
127,077✔
424
    }
127,077✔
425

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

431
    size_t find_all_local(size_t start, size_t end) override
432
    {
429,339✔
433
        return BaseType::template find_all_local<TConditionFunction>(start, end);
429,339✔
434
    }
429,339✔
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
    {
127,077✔
443
        return std::unique_ptr<ParentNode>(new ThisType(*this));
127,077✔
444
    }
127,077✔
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,216,044✔
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,216,044✔
453
        for (size_t i = start; i < end; ++i) {
1,832,145✔
454
            auto element = leaf.get(i);
1,620,156✔
455
            if (std::find(needles.begin(), needles.end(), element) != needles.end())
1,620,156✔
456
                return i;
946,371✔
457
        }
1,620,156✔
458
    }
1,158,360✔
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;
212,052✔
467
}
1,216,044✔
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
{
450✔
473
    if (needles.size() < linear_search_threshold) {
450✔
474
        for (size_t i = start; i < end; ++i) {
16,812✔
475
            auto element = leaf.get(i);
16,542✔
476
            if (std::find(needles.begin(), needles.end(), element) != needles.end())
16,542✔
477
                state->match(i);
582✔
478
        }
16,542✔
479
    }
270✔
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;
450✔
488
}
450✔
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)
781,641✔
499
    {
1,560,390✔
500
    }
1,560,390✔
501
    IntegerNode(ColKey col, const Mixed* begin, const Mixed* end)
502
        : BaseType(realm::npos, col)
225✔
503
    {
438✔
504
        static_assert(is_any_v<TConditionValue, std::optional<int64_t>, int64_t>, "unexpected type change");
438✔
505
        for (const Mixed* it = begin; it != end; ++it) {
14,469✔
506
            if constexpr (std::is_same_v<TConditionValue, std::optional<int64_t>>) {
14,028✔
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,025✔
513
                m_needles.insert(*val);
13,962✔
514
            }
13,962✔
515
        }
11,331✔
516
    }
438✔
517

518
    void init(bool will_query_ranges) override
519
    {
1,529,805✔
520
        BaseType::init(will_query_ranges);
1,529,805✔
521
        m_nb_needles = m_needles.size();
1,529,805✔
522

523
        if (has_search_index() && m_nb_needles == 0) {
1,529,805✔
524
            SearchIndex* index = ParentNode::m_table->get_search_index(ParentNode::m_condition_column_key);
9,876✔
525
            m_index_evaluator = IndexEvaluator();
9,876✔
526
            m_index_evaluator->init(index, BaseType::m_value);
9,876✔
527
            IntegerNodeBase<LeafType>::m_dT = 0;
9,876✔
528
        }
9,876✔
529
    }
1,529,805✔
530

531
    bool do_consume_condition(ParentNode& node) override
532
    {
29,937✔
533
        auto& other = static_cast<ThisType&>(node);
29,937✔
534
        REALM_ASSERT(this->m_condition_column_key == other.m_condition_column_key);
29,937✔
535
        if (m_needles.empty()) {
29,937✔
536
            m_needles.insert(this->m_value);
23,919✔
537
        }
23,919✔
538
        if (other.m_needles.empty()) {
29,937✔
539
            m_needles.insert(other.m_value);
29,919✔
540
        }
29,919✔
541
        else {
18✔
542
            for (const auto& val : other.m_needles) {
18✔
543
                m_needles.insert(val);
18✔
544
            }
18✔
545
        }
18✔
546
        return true;
29,937✔
547
    }
29,937✔
548

549
    bool has_search_index() const override
550
    {
1,562,883✔
551
        return this->m_table->search_index_type(IntegerNodeBase<LeafType>::m_condition_column_key) ==
1,562,883✔
552
               IndexType::General;
1,562,883✔
553
    }
1,562,883✔
554

555
    const IndexEvaluator* index_based_keys() override
556
    {
1,550,091✔
557
        return m_index_evaluator ? &(*m_index_evaluator) : nullptr;
1,550,091✔
558
    }
1,550,091✔
559

560
    size_t find_first_local(size_t start, size_t end) override
561
    {
4,500,399✔
562
        REALM_ASSERT(this->m_table);
4,500,399✔
563
        size_t s = realm::npos;
4,500,399✔
564

565
        if (start < end) {
4,500,399✔
566
            if (m_nb_needles) {
4,498,077✔
567
                s = find_first_haystack<22>(*this->m_leaf, m_needles, start, end);
1,192,938✔
568
            }
1,192,938✔
569
            else if (m_index_evaluator) {
3,305,139✔
570
                return m_index_evaluator->do_search_index(BaseType::m_cluster, start, end);
1,764✔
571
            }
1,764✔
572
            else if (end - start == 1) {
3,303,375✔
573
                if (this->m_leaf->get(start) == this->m_value) {
1,293,852✔
574
                    s = start;
567,897✔
575
                }
567,897✔
576
            }
1,293,852✔
577
            else {
2,009,523✔
578
                s = this->m_leaf->template find_first<Equal>(this->m_value, start, end);
2,009,523✔
579
            }
2,009,523✔
580
        }
4,498,077✔
581

582
        return s;
4,498,635✔
583
    }
4,500,399✔
584

585
    size_t find_all_local(size_t start, size_t end) override
586
    {
5,715,396✔
587
        if (m_nb_needles) {
5,715,396✔
588
            return find_all_haystack<22>(*this->m_leaf, m_needles, start, end, ParentNode::m_state);
450✔
589
        }
450✔
590
        return BaseType::template find_all_local<Equal>(start, end);
5,714,946✔
591
    }
5,715,396✔
592

593
    std::string describe(util::serializer::SerialisationState& state) const override
594
    {
12,894✔
595
        REALM_ASSERT(this->m_condition_column_key);
12,894✔
596
        std::string col_descr = state.describe_column(this->m_table, this->m_condition_column_key);
12,894✔
597

598
        if (m_needles.empty()) {
12,894✔
599
            return col_descr + " " + Equal::description() + " " +
12,732✔
600
                   util::serializer::print_value(IntegerNodeBase<LeafType>::m_value);
12,732✔
601
        }
12,732✔
602

603
        std::string list_contents;
162✔
604
        bool is_first = true;
162✔
605
        for (auto it : m_needles) {
342✔
606
            list_contents +=
342✔
607
                util::format("%1%2", is_first ? "" : ", ", util::serializer::print_value(it)); // "it" may be null
342✔
608
            is_first = false;
342✔
609
        }
342✔
610
        std::string desc = util::format("%1 IN {%2}", col_descr, list_contents);
162✔
611
        return desc;
162✔
612
    }
12,894✔
613

614
    std::unique_ptr<ParentNode> clone() const override
615
    {
1,563,273✔
616
        return std::unique_ptr<ParentNode>(new ThisType(*this));
1,563,273✔
617
    }
1,563,273✔
618

619
private:
620
    std::unordered_set<TConditionValue> m_needles;
621
    size_t m_nb_needles = 0;
622
    std::optional<IndexEvaluator> m_index_evaluator;
623

624
    IntegerNode(const IntegerNode<LeafType, Equal>& from)
625
        : BaseType(from)
778,011✔
626
        , m_needles(from.m_needles)
778,011✔
627
    {
1,571,124✔
628
    }
1,571,124✔
629
};
630

631

632
// This node is currently used for floats and doubles only
633
template <class LeafType, class TConditionFunction>
634
class FloatDoubleNode : public ParentNode {
635
public:
636
    using TConditionValue = typename LeafType::value_type;
637
    static const bool special_null_node = false;
638

639
    FloatDoubleNode(TConditionValue v, ColKey column_key)
640
        : m_value(v)
13,314✔
641
    {
26,628✔
642
        m_condition_column_key = column_key;
26,628✔
643
        m_dT = 1.0;
26,628✔
644
    }
26,628✔
645
    FloatDoubleNode(null, ColKey column_key)
646
        : m_value(null::get_null_float<TConditionValue>())
300✔
647
    {
600✔
648
        m_condition_column_key = column_key;
600✔
649
        m_dT = 1.0;
600✔
650
    }
600✔
651

652
    void cluster_changed() override
653
    {
43,200✔
654
        m_leaf.emplace(m_table.unchecked_ptr()->get_alloc());
43,200✔
655
        m_cluster->init_leaf(this->m_condition_column_key, &*m_leaf);
43,200✔
656
    }
43,200✔
657

658
    size_t find_first_local(size_t start, size_t end) override
659
    {
385,320✔
660
        TConditionFunction cond;
385,320✔
661

662
        auto find = [&](bool nullability) {
385,320✔
663
            bool value_nan = nullability ? null::is_null_float(m_value) : false;
385,320!
664
            for (size_t s = start; s < end; ++s) {
4,072,689!
665
                TConditionValue v = m_leaf->get(s);
3,985,941✔
666
                REALM_ASSERT(!(null::is_null_float(v) && !nullability));
3,985,941!
667
                if (cond(v, m_value, nullability ? null::is_null_float<TConditionValue>(v) : false, value_nan))
3,985,941!
668
                    return s;
298,572✔
669
            }
3,985,941✔
670
            return not_found;
86,748✔
671
        };
385,320✔
672

673
        // This will inline the second case but no the first. Todo, use templated lambda when switching to c++14
674
        if (m_table->is_nullable(m_condition_column_key))
385,320!
675
            return find(true);
20,832✔
676
        else
364,488✔
677
            return find(false);
364,488✔
678
    }
385,320✔
679

680
    std::string describe(util::serializer::SerialisationState& state) const override
681
    {
270✔
682
        REALM_ASSERT(m_condition_column_key);
270!
683
        return state.describe_column(ParentNode::m_table, m_condition_column_key) + " " + describe_condition() + " " +
270✔
684
               util::serializer::print_value(FloatDoubleNode::m_value);
270✔
685
    }
270✔
686
    std::string describe_condition() const override
687
    {
270✔
688
        return TConditionFunction::description();
270✔
689
    }
270✔
690

691
    std::unique_ptr<ParentNode> clone() const override
692
    {
26,142✔
693
        return std::unique_ptr<ParentNode>(new FloatDoubleNode(*this));
26,142✔
694
    }
26,142✔
695

696
    FloatDoubleNode(const FloatDoubleNode& from)
697
        : ParentNode(from)
13,071✔
698
        , m_value(from.m_value)
13,071✔
699
    {
26,142✔
700
    }
26,142✔
701

702
protected:
703
    TConditionValue m_value;
704
    std::optional<LeafType> m_leaf;
705
};
706

707
template <class T, class TConditionFunction>
708
class SizeNode : public ParentNode {
709
public:
710
    SizeNode(int64_t v, ColKey column)
711
        : m_value(v)
6✔
712
    {
12✔
713
        m_condition_column_key = column;
12✔
714
        m_dT = 20.0;
12✔
715
    }
12✔
716

717
    void cluster_changed() override
718
    {
12✔
719
        m_leaf.emplace(m_table.unchecked_ptr()->get_alloc());
12✔
720
        m_cluster->init_leaf(m_condition_column_key, &*m_leaf);
12✔
721
    }
12✔
722

723
    size_t find_first_local(size_t start, size_t end) override
724
    {
18✔
725
        for (size_t s = start; s < end; ++s) {
78!
726
            if (T v = m_leaf->get(s)) {
72!
727
                int64_t sz = v.size();
24✔
728
                if (TConditionFunction()(sz, m_value))
24!
729
                    return s;
12✔
730
            }
24✔
731
        }
72✔
732
        return not_found;
6✔
733
    }
18✔
734

735
    std::unique_ptr<ParentNode> clone() const override
736
    {
18✔
737
        return std::unique_ptr<ParentNode>(new SizeNode(*this));
18✔
738
    }
18✔
739

740
    SizeNode(const SizeNode& from)
741
        : ParentNode(from)
9✔
742
        , m_value(from.m_value)
9✔
743
    {
18✔
744
    }
18✔
745

746
private:
747
    using LeafType = typename ColumnTypeTraits<T>::cluster_leaf_type;
748
    std::optional<LeafType> m_leaf;
749
    int64_t m_value;
750
};
751

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

754
template <class TConditionFunction>
755
class SizeListNode : public ParentNode {
756
public:
757
    SizeListNode(int64_t v, ColKey column)
758
        : m_value(v)
72✔
759
    {
144✔
760
        m_condition_column_key = column;
144✔
761
        m_dT = 30.0;
144✔
762
    }
144✔
763

764
    void reset_cache()
765
    {
288✔
766
        m_cached_col_type = m_condition_column_key.get_type();
288✔
767
        m_cached_nullable = m_condition_column_key.is_nullable();
288✔
768
        REALM_ASSERT_DEBUG(m_condition_column_key.is_list());
288✔
769
    }
288✔
770

771
    void cluster_changed() override
772
    {
144✔
773
        m_leaf.emplace(m_table.unchecked_ptr()->get_alloc());
144✔
774
        m_cluster->init_leaf(m_condition_column_key, &*m_leaf);
144✔
775
        reset_cache();
144✔
776
    }
144✔
777

778
    void init(bool will_query_ranges) override
779
    {
144✔
780
        ParentNode::init(will_query_ranges);
144✔
781
        reset_cache();
144✔
782
    }
144✔
783

784
    size_t find_first_local(size_t start, size_t end) override
785
    {
204✔
786
        Allocator& alloc = m_table.unchecked_ptr()->get_alloc();
204✔
787
        for (size_t s = start; s < end; ++s) {
294✔
788
            if (ref_type ref = m_leaf->get(s)) {
276✔
789
                int64_t sz = size_of_list_from_ref(ref, alloc, m_cached_col_type, m_cached_nullable);
204✔
790
                if (TConditionFunction()(sz, m_value))
204✔
791
                    return s;
186✔
792
            }
204✔
793
        }
276✔
794
        return not_found;
18✔
795
    }
204✔
796

797
    std::unique_ptr<ParentNode> clone() const override
798
    {
162✔
799
        return std::unique_ptr<ParentNode>(new SizeListNode(*this));
162✔
800
    }
162✔
801

802
    SizeListNode(const SizeListNode& from)
803
        : ParentNode(from)
81✔
804
        , m_value(from.m_value)
81✔
805
    {
162✔
806
    }
162✔
807

808
private:
809
    std::optional<ArrayList> m_leaf;
810

811
    int64_t m_value;
812

813
    ColumnType m_cached_col_type;
814
    bool m_cached_nullable;
815
};
816

817

818
template <class TConditionFunction>
819
class BinaryNode : public ParentNode {
820
public:
821
    using TConditionValue = BinaryData;
822
    static const bool special_null_node = false;
823

824
    BinaryNode(BinaryData v, ColKey column)
825
        : m_value(v)
7,368✔
826
    {
14,736✔
827
        m_condition_column_key = column;
14,736✔
828
        m_dT = 100.0;
14,736✔
829
    }
14,736✔
830

831
    BinaryNode(null, ColKey column)
832
        : BinaryNode(BinaryData{}, column)
201✔
833
    {
402✔
834
    }
402✔
835

836
    void cluster_changed() override
837
    {
20,046✔
838
        m_leaf.emplace(m_table.unchecked_ptr()->get_alloc());
20,046✔
839
        m_cluster->init_leaf(m_condition_column_key, &*m_leaf);
20,046✔
840
    }
20,046✔
841

842
    size_t find_first_local(size_t start, size_t end) override
843
    {
71,916✔
844
        TConditionFunction condition;
71,916✔
845
        for (size_t s = start; s < end; ++s) {
3,033,633!
846
            BinaryData value = m_leaf->get(s);
3,015,957✔
847
            if (condition(m_value.get(), value))
3,015,957!
848
                return s;
54,240✔
849
        }
3,015,957✔
850
        return not_found;
17,676✔
851
    }
71,916✔
852

853
    std::string describe(util::serializer::SerialisationState& state) const override
854
    {
2,286✔
855
        REALM_ASSERT(m_condition_column_key);
2,286!
856
        return state.describe_column(ParentNode::m_table, m_condition_column_key) + " " +
2,286✔
857
               TConditionFunction::description() + " " + util::serializer::print_value(BinaryNode::m_value.get());
2,286✔
858
    }
2,286✔
859

860
    std::unique_ptr<ParentNode> clone() const override
861
    {
16,728✔
862
        return std::unique_ptr<ParentNode>(new BinaryNode(*this));
16,728✔
863
    }
16,728✔
864

865
    BinaryNode(const BinaryNode& from)
866
        : ParentNode(from)
8,364✔
867
        , m_value(from.m_value)
8,364✔
868
    {
16,728✔
869
    }
16,728✔
870

871
private:
872
    OwnedBinaryData m_value;
873
    std::optional<ArrayBinary> m_leaf;
874
};
875

876
template <class TConditionFunction>
877
class BoolNode : public ParentNode {
878
public:
879
    using TConditionValue = bool;
880

881
    BoolNode(util::Optional<bool> v, ColKey column)
882
        : m_value(v)
1,035✔
883
    {
2,070✔
884
        m_condition_column_key = column;
2,070✔
885
    }
2,070✔
886

887
    BoolNode(const BoolNode& from)
888
        : ParentNode(from)
1,008✔
889
        , m_value(from.m_value)
1,008✔
890
        , m_index_evaluator(from.m_index_evaluator)
1,008✔
891
    {
2,016✔
892
    }
2,016✔
893

894
    void init(bool will_query_ranges) override
895
    {
3,126✔
896
        ParentNode::init(will_query_ranges);
3,126✔
897

898
        if constexpr (std::is_same_v<TConditionFunction, Equal>) {
3,126✔
899
            if (m_index_evaluator) {
1,890✔
900
                SearchIndex* index = m_table->get_search_index(m_condition_column_key);
×
901
                m_index_evaluator->init(index, m_value);
×
902
                this->m_dT = 0;
×
903
            }
×
904
        }
1,890✔
905
    }
3,126✔
906

907
    void table_changed() override
908
    {
2,538✔
909
        if constexpr (std::is_same_v<TConditionFunction, Equal>) {
2,538✔
910
            const bool has_index = m_table->search_index_type(m_condition_column_key) == IndexType::General;
1,482✔
911
            m_index_evaluator = has_index ? std::make_optional(IndexEvaluator{}) : std::nullopt;
1,482✔
912
        }
1,482✔
913
    }
2,538✔
914

915
    const IndexEvaluator* index_based_keys() override
916
    {
2,964✔
917
        return m_index_evaluator ? &(*m_index_evaluator) : nullptr;
2,964!
918
    }
2,964✔
919

920
    bool has_search_index() const override
921
    {
60✔
922
        return bool(m_index_evaluator);
60✔
923
    }
60✔
924

925
    void cluster_changed() override
926
    {
3,480✔
927
        m_leaf.emplace(m_table.unchecked_ptr()->get_alloc());
3,480✔
928
        m_cluster->init_leaf(m_condition_column_key, &*m_leaf);
3,480✔
929
    }
3,480✔
930

931
    size_t find_first_local(size_t start, size_t end) override
932
    {
31,236✔
933
        if constexpr (std::is_same_v<TConditionFunction, Equal>) {
31,236✔
934
            if (m_index_evaluator) {
29,964✔
935
                return m_index_evaluator->do_search_index(m_cluster, start, end);
×
936
            }
×
937
        }
29,964✔
938

939
        TConditionFunction condition;
29,964✔
940
        bool m_value_is_null = !m_value;
30,600✔
941
        for (size_t s = start; s < end; ++s) {
56,664!
942
            auto value = m_leaf->get(s);
54,942✔
943
            if (condition(value, m_value, !value, m_value_is_null))
54,942!
944
                return s;
29,514✔
945
        }
54,942✔
946
        return not_found;
1,722✔
947
    }
30,600✔
948

949
    std::string describe(util::serializer::SerialisationState& state) const override
950
    {
96✔
951
        return state.describe_column(ParentNode::m_table, m_condition_column_key) + " " +
96✔
952
               TConditionFunction::description() + " " + util::serializer::print_value(m_value);
96✔
953
    }
96✔
954

955
    std::unique_ptr<ParentNode> clone() const override
956
    {
2,016✔
957
        return std::unique_ptr<ParentNode>(new BoolNode(*this));
2,016✔
958
    }
2,016✔
959

960
private:
961
    std::optional<bool> m_value;
962
    std::optional<ArrayBoolNull> m_leaf;
963
    std::optional<IndexEvaluator> m_index_evaluator;
964
};
965

966
class TimestampNodeBase : public ParentNode {
967
public:
968
    using TConditionValue = Timestamp;
969
    static const bool special_null_node = false;
970

971
    TimestampNodeBase(Timestamp v, ColKey column)
972
        : m_value(v)
6,723✔
973
    {
13,446✔
974
        m_condition_column_key = column;
13,446✔
975
        m_dT = 2.0;
13,446✔
976
    }
13,446✔
977

978
    TimestampNodeBase(null, ColKey column)
979
        : TimestampNodeBase(Timestamp{}, column)
144✔
980
    {
288✔
981
    }
288✔
982

983
    void cluster_changed() override
984
    {
15,810✔
985
        m_leaf.emplace(m_table.unchecked_ptr()->get_alloc());
15,810✔
986
        m_cluster->init_leaf(m_condition_column_key, &*m_leaf);
15,810✔
987
    }
15,810✔
988

989
protected:
990
    TimestampNodeBase(const TimestampNodeBase& from)
991
        : ParentNode(from)
6,360✔
992
        , m_value(from.m_value)
6,360✔
993
    {
12,720✔
994
    }
12,720✔
995

996
    Timestamp m_value;
997
    std::optional<ArrayTimestamp> m_leaf;
998
};
999

1000
template <class TConditionFunction>
1001
class TimestampNode : public TimestampNodeBase {
1002
public:
1003
    using TimestampNodeBase::TimestampNodeBase;
1004

1005
    void init(bool will_query_ranges) override
1006
    {
15,942✔
1007
        TimestampNodeBase::init(will_query_ranges);
15,942✔
1008

1009
        if constexpr (std::is_same_v<TConditionFunction, Equal>) {
15,942✔
1010
            if (m_index_evaluator) {
9,378✔
1011
                SearchIndex* index =
132✔
1012
                    TimestampNodeBase::m_table->get_search_index(TimestampNodeBase::m_condition_column_key);
132✔
1013
                m_index_evaluator->init(index, TimestampNodeBase::m_value);
132✔
1014
                this->m_dT = 0;
132✔
1015
            }
132✔
1016
        }
9,378✔
1017
    }
15,942✔
1018

1019
    void table_changed() override
1020
    {
14,814✔
1021
        if constexpr (std::is_same_v<TConditionFunction, Equal>) {
14,814✔
1022
            const bool has_index =
9,180✔
1023
                this->m_table->search_index_type(TimestampNodeBase::m_condition_column_key) == IndexType::General;
9,180✔
1024
            m_index_evaluator = has_index ? std::make_optional(IndexEvaluator{}) : std::nullopt;
9,180✔
1025
        }
9,180✔
1026
    }
14,814✔
1027

1028
    const IndexEvaluator* index_based_keys() override
1029
    {
8,436✔
1030
        return m_index_evaluator ? &*m_index_evaluator : nullptr;
8,436✔
1031
    }
8,436✔
1032

1033
    bool has_search_index() const override
1034
    {
7,242✔
1035
        return bool(m_index_evaluator);
7,242✔
1036
    }
7,242✔
1037

1038
    size_t find_first_local(size_t start, size_t end) override
1039
    {
59,040✔
1040
        if constexpr (std::is_same_v<TConditionFunction, Equal>) {
59,040✔
1041
            if (m_index_evaluator) {
21,024✔
1042
                return m_index_evaluator->do_search_index(this->m_cluster, start, end);
×
1043
            }
×
1044
        }
21,024✔
1045
        return m_leaf->find_first<TConditionFunction>(m_value, start, end);
21,024✔
1046
    }
59,040✔
1047

1048
    std::string describe(util::serializer::SerialisationState& state) const override
1049
    {
156✔
1050
        REALM_ASSERT(m_condition_column_key);
156!
1051
        return state.describe_column(ParentNode::m_table, m_condition_column_key) + " " +
156✔
1052
               TConditionFunction::description() + " " + util::serializer::print_value(TimestampNode::m_value);
156✔
1053
    }
156✔
1054

1055
    std::unique_ptr<ParentNode> clone() const override
1056
    {
12,720✔
1057
        return std::unique_ptr<ParentNode>(new TimestampNode(*this));
12,720✔
1058
    }
12,720✔
1059

1060
protected:
1061
    std::optional<IndexEvaluator> m_index_evaluator;
1062
};
1063

1064
class DecimalNodeBase : public ParentNode {
1065
public:
1066
    using TConditionValue = Decimal128;
1067
    static const bool special_null_node = false;
1068

1069
    DecimalNodeBase(Decimal128 v, ColKey column)
1070
        : m_value(v)
4,344✔
1071
    {
8,688✔
1072
        m_condition_column_key = column;
8,688✔
1073
    }
8,688✔
1074

1075
    DecimalNodeBase(null, ColKey column)
1076
        : DecimalNodeBase(Decimal128{null()}, column)
6✔
1077
    {
12✔
1078
    }
12✔
1079

1080
    void cluster_changed() override
1081
    {
8,682✔
1082
        m_leaf.emplace(m_table.unchecked_ptr()->get_alloc());
8,682✔
1083
        m_cluster->init_leaf(m_condition_column_key, &*m_leaf);
8,682✔
1084
    }
8,682✔
1085

1086
    void init(bool will_query_ranges) override
1087
    {
8,682✔
1088
        ParentNode::init(will_query_ranges);
8,682✔
1089

1090
        m_dD = 100.0;
8,682✔
1091
    }
8,682✔
1092

1093
protected:
1094
    DecimalNodeBase(const DecimalNodeBase& from)
1095
        : ParentNode(from)
5,013✔
1096
        , m_value(from.m_value)
5,013✔
1097
    {
10,026✔
1098
    }
10,026✔
1099

1100
    Decimal128 m_value;
1101
    std::optional<ArrayDecimal128> m_leaf;
1102
};
1103

1104
template <class TConditionFunction>
1105
class DecimalNode : public DecimalNodeBase {
1106
public:
1107
    using DecimalNodeBase::DecimalNodeBase;
1108

1109
    size_t find_first_local(size_t start, size_t end) override
1110
    {
19,422✔
1111
        TConditionFunction cond;
19,422✔
1112
        bool value_is_null = m_value.is_null();
19,422✔
1113
        for (size_t i = start; i < end; i++) {
1,509,378!
1114
            Decimal128 val = m_leaf->get(i);
1,500,666✔
1115
            if (cond(val, m_value, val.is_null(), value_is_null))
1,500,666!
1116
                return i;
10,710✔
1117
        }
1,500,666✔
1118
        return realm::npos;
8,712✔
1119
    }
19,422✔
1120

1121
    std::string describe(util::serializer::SerialisationState& state) const override
1122
    {
612✔
1123
        REALM_ASSERT(m_condition_column_key);
612!
1124
        return state.describe_column(ParentNode::m_table, m_condition_column_key) + " " +
612✔
1125
               TConditionFunction::description() + " " + util::serializer::print_value(DecimalNode::m_value);
612✔
1126
    }
612✔
1127

1128
    std::unique_ptr<ParentNode> clone() const override
1129
    {
10,026✔
1130
        return std::unique_ptr<ParentNode>(new DecimalNode(*this));
10,026✔
1131
    }
10,026✔
1132
};
1133

1134
template <class ObjectType, class ArrayType>
1135
class FixedBytesNodeBase : public ParentNode {
1136
public:
1137
    using TConditionValue = ObjectType;
1138
    static const bool special_null_node = false;
1139

1140
    FixedBytesNodeBase(ObjectType v, ColKey column)
1141
        : m_value(v)
322,485✔
1142
    {
644,970✔
1143
        m_condition_column_key = column;
644,970✔
1144
    }
644,970✔
1145

1146
    FixedBytesNodeBase(null, ColKey column)
1147
        : FixedBytesNodeBase(ObjectType{}, column)
183✔
1148
    {
366✔
1149
        m_value_is_null = true;
366✔
1150
    }
366✔
1151

1152
    void cluster_changed() override
1153
    {
216,711✔
1154
        m_leaf.emplace(m_table.unchecked_ptr()->get_alloc());
216,711✔
1155
        m_cluster->init_leaf(m_condition_column_key, &*m_leaf);
216,711✔
1156
    }
216,711✔
1157

1158
    void init(bool will_query_ranges) override
1159
    {
216,579✔
1160
        ParentNode::init(will_query_ranges);
216,579✔
1161

1162
        m_dD = 100.0;
216,579✔
1163
    }
216,579✔
1164

1165
protected:
1166
    FixedBytesNodeBase(const FixedBytesNodeBase& from)
1167
        : ParentNode(from)
643,686✔
1168
        , m_value(from.m_value)
643,686✔
1169
        , m_value_is_null(from.m_value_is_null)
643,686✔
1170
    {
1,287,372✔
1171
    }
1,287,372✔
1172

1173
    ObjectType m_value;
1174
    std::optional<ArrayType> m_leaf;
1175
    bool m_value_is_null = false;
1176
};
1177

1178
template <class TConditionFunction, class ObjectType, class ArrayType>
1179
class FixedBytesNode : public FixedBytesNodeBase<ObjectType, ArrayType> {
1180
public:
1181
    using FixedBytesNodeBase<ObjectType, ArrayType>::FixedBytesNodeBase;
1182

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

1200
    std::string describe(util::serializer::SerialisationState& state) const override
1201
    {
396✔
1202
        REALM_ASSERT(this->m_condition_column_key);
396!
1203
        return state.describe_column(ParentNode::m_table, this->m_condition_column_key) + " " +
396✔
1204
               TConditionFunction::description() + " " +
396✔
1205
               (this->m_value_is_null ? util::serializer::print_value(realm::null())
396!
1206
                                      : util::serializer::print_value(this->m_value));
396✔
1207
    }
396✔
1208

1209
    std::unique_ptr<ParentNode> clone() const override
1210
    {
1,632✔
1211
        return std::unique_ptr<ParentNode>(new FixedBytesNode(*this));
1,632✔
1212
    }
1,632✔
1213
};
1214

1215
template <class ObjectType, class ArrayType>
1216
class FixedBytesNode<Equal, ObjectType, ArrayType> : public FixedBytesNodeBase<ObjectType, ArrayType> {
1217
public:
1218
    using FixedBytesNodeBase<ObjectType, ArrayType>::FixedBytesNodeBase;
1219
    using BaseType = FixedBytesNodeBase<ObjectType, ArrayType>;
1220
    using ThisType = FixedBytesNode<Equal, ObjectType, ArrayType>;
1221

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

1241
    void init(bool will_query_ranges) override
1242
    {
215,583✔
1243
        BaseType::init(will_query_ranges);
215,583✔
1244
        m_nb_needles = m_needles.size();
215,583✔
1245

1246
        if (!this->m_value_is_null) {
215,583✔
1247
            m_optional_value = this->m_value;
215,253✔
1248
        }
215,253✔
1249
        if (m_index_evaluator && m_nb_needles == 0) {
215,583✔
1250
            SearchIndex* index = BaseType::m_table->get_search_index(BaseType::m_condition_column_key);
214,827✔
1251
            m_index_evaluator->init(index, m_optional_value);
214,827✔
1252
            this->m_dT = 0;
214,827✔
1253
        }
214,827✔
1254
    }
215,583✔
1255

1256
    void table_changed() override
1257
    {
643,986✔
1258
        const bool has_index =
643,986✔
1259
            this->m_table->search_index_type(BaseType::m_condition_column_key) == IndexType::General;
643,986✔
1260
        m_index_evaluator = has_index ? std::make_optional(IndexEvaluator{}) : std::nullopt;
643,986✔
1261
    }
643,986✔
1262

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

1268
    bool has_search_index() const override
1269
    {
642,120✔
1270
        return bool(m_index_evaluator);
642,120✔
1271
    }
642,120✔
1272

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

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

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

1296
        return s;
13,242✔
1297
    }
242,349✔
1298

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

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

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

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

1348

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1619
    StringNodeBase(const StringNodeBase& from)
1620
        : ParentNode(from)
27,966✔
1621
        , m_value(from.m_value)
27,966✔
1622
        , m_string_value(m_value)
27,966✔
1623
        , m_is_string_enum(from.m_is_string_enum)
27,966✔
1624
    {
56,361✔
1625
    }
56,361✔
1626

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

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

1639
    bool m_is_string_enum = false;
1640

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1752

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

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

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

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

1771

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

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

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

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

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

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

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

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

1829

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

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

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

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

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

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

1871
class StringNodeEqualBase : public StringNodeBase {
1872
public:
1873
    StringNodeEqualBase(StringData v, ColKey column)
1874
        : StringNodeBase(v, column)
18,894✔
1875
    {
38,214✔
1876
    }
38,214✔
1877
    StringNodeEqualBase(const StringNodeEqualBase& from)
1878
        : StringNodeBase(from)
22,398✔
1879
        , m_index_evaluator(from.m_index_evaluator)
22,398✔
1880
    {
45,225✔
1881
    }
45,225✔
1882

1883
    void init(bool) override;
1884

1885
    void table_changed() override
1886
    {
39,018✔
1887
        StringNodeBase::table_changed();
39,018✔
1888
        const bool has_index =
39,018✔
1889
            m_table.unchecked_ptr()->search_index_type(m_condition_column_key) == IndexType::General;
39,018✔
1890
        m_index_evaluator = has_index ? std::make_optional(IndexEvaluator{}) : std::nullopt;
39,018✔
1891
    }
39,018✔
1892

1893
    bool has_search_index() const override
1894
    {
65,484✔
1895
        return bool(m_index_evaluator);
65,484✔
1896
    }
65,484✔
1897

1898
    void cluster_changed() override
1899
    {
304,224✔
1900
        // If we use searchindex, we do not need further access to clusters
1901
        if (!m_index_evaluator) {
304,224✔
1902
            StringNodeBase::cluster_changed();
92,202✔
1903
        }
92,202✔
1904
    }
304,224✔
1905

1906
    size_t find_first_local(size_t start, size_t end) override;
1907

1908
    std::string describe_condition() const override
1909
    {
6,477✔
1910
        return Equal::description();
6,477✔
1911
    }
6,477✔
1912

1913
    const IndexEvaluator* index_based_keys() override
1914
    {
43,221✔
1915
        return m_index_evaluator ? &(*m_index_evaluator) : nullptr;
43,221✔
1916
    }
43,221✔
1917

1918
protected:
1919
    std::optional<IndexEvaluator> m_index_evaluator;
1920

1921
    inline BinaryData str_to_bin(const StringData& s) noexcept
1922
    {
×
1923
        return BinaryData(s.data(), s.size());
×
1924
    }
×
1925

1926
    virtual void _search_index_init() = 0;
1927
    virtual size_t _find_first_local(size_t start, size_t end) = 0;
1928
};
1929

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

1943
    void _search_index_init() override;
1944

1945
    bool do_consume_condition(ParentNode& other) override;
1946

1947
    std::unique_ptr<ParentNode> clone() const override
1948
    {
43,827✔
1949
        return std::unique_ptr<ParentNode>(new StringNode<Equal>(*this));
43,827✔
1950
    }
43,827✔
1951

1952
    std::string describe(util::serializer::SerialisationState& state) const override;
1953

1954
    StringNode(const StringNode& from)
1955
        : StringNodeEqualBase(from)
21,699✔
1956
    {
43,827✔
1957
        for (auto& needle : from.m_needles) {
43,827✔
1958
            if (needle.is_null()) {
648✔
1959
                m_needles.emplace();
102✔
1960
            }
102✔
1961
            else {
546✔
1962
                m_needle_storage.push_back(std::make_unique<char[]>(needle.size()));
546✔
1963
                std::copy(needle.data(), needle.data() + needle.size(), m_needle_storage.back().get());
546✔
1964
                m_needles.insert(StringData(m_needle_storage.back().get(), needle.size()));
546✔
1965
            }
546✔
1966
        }
648✔
1967
    }
43,827✔
1968

1969
private:
1970
    size_t _find_first_local(size_t start, size_t end) override;
1971
    std::unordered_set<StringData> m_needles;
1972
    std::vector<std::unique_ptr<char[]>> m_needle_storage;
1973
};
1974

1975

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

1995
    void _search_index_init() override;
1996

1997
    std::string describe_condition() const override
1998
    {
12✔
1999
        return EqualIns::description();
12✔
2000
    }
12✔
2001

2002
    std::unique_ptr<ParentNode> clone() const override
2003
    {
930✔
2004
        return std::unique_ptr<ParentNode>(new StringNode(*this));
930✔
2005
    }
930✔
2006

2007
    StringNode(const StringNode& from)
2008
        : StringNodeEqualBase(from)
465✔
2009
        , m_ucase(from.m_ucase)
465✔
2010
        , m_lcase(from.m_lcase)
465✔
2011
    {
930✔
2012
    }
930✔
2013

2014
private:
2015
    std::vector<ObjKey> m_index_matches;
2016
    std::string m_ucase;
2017
    std::string m_lcase;
2018
    std::vector<ObjKey> storage;
2019
    size_t _find_first_local(size_t start, size_t end) override;
2020
};
2021

2022

2023
class StringNodeFulltext : public StringNodeEqualBase {
2024
public:
2025
    StringNodeFulltext(StringData v, ColKey column, std::unique_ptr<LinkMap> lm = {});
2026

2027
    void table_changed() override;
2028

2029
    void _search_index_init() override;
2030

2031
    bool has_search_index() const override
2032
    {
378✔
2033
        return true; // it's a required precondition for fulltext queries
378✔
2034
    }
378✔
2035

2036
    std::unique_ptr<ParentNode> clone() const override
2037
    {
468✔
2038
        return std::unique_ptr<ParentNode>(new StringNodeFulltext(*this));
468✔
2039
    }
468✔
2040

2041
    std::string describe_condition() const override
2042
    {
×
2043
        return "FULLTEXT";
×
2044
    }
×
2045

2046
private:
2047
    std::vector<ObjKey> m_index_matches;
2048
    std::unique_ptr<LinkMap> m_link_map;
2049
    StringNodeFulltext(const StringNodeFulltext&);
2050

2051
    size_t _find_first_local(size_t, size_t) override
2052
    {
×
2053
        REALM_UNREACHABLE();
2054
    }
×
2055
};
2056

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

2073
    OrNode(const OrNode& other)
2074
        : ParentNode(other)
13,926✔
2075
    {
27,705✔
2076
        for (const auto& condition : other.m_conditions) {
740,136✔
2077
            m_conditions.emplace_back(condition->clone());
740,136✔
2078
        }
740,136✔
2079
    }
27,705✔
2080

2081
    void table_changed() override
2082
    {
26,739✔
2083
        for (auto& condition : m_conditions) {
26,739✔
2084
            condition->set_table(m_table);
25,887✔
2085
        }
25,887✔
2086
    }
26,739✔
2087

2088
    void cluster_changed() override
2089
    {
57,399✔
2090
        for (auto& condition : m_conditions) {
338,175✔
2091
            condition->set_cluster(m_cluster);
338,175✔
2092
        }
338,175✔
2093

2094
        m_start.clear();
57,399✔
2095
        m_start.resize(m_conditions.size(), 0);
57,399✔
2096

2097
        m_last.clear();
57,399✔
2098
        m_last.resize(m_conditions.size(), 0);
57,399✔
2099

2100
        m_was_match.clear();
57,399✔
2101
        m_was_match.resize(m_conditions.size(), false);
57,399✔
2102
    }
57,399✔
2103

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

2121
    void collect_dependencies(std::vector<TableKey>& versions) const override
2122
    {
24,507✔
2123
        for (const auto& cond : m_conditions) {
25,185✔
2124
            cond->collect_dependencies(versions);
25,185✔
2125
        }
25,185✔
2126
    }
24,507✔
2127

2128
    void init(bool will_query_ranges) override
2129
    {
26,781✔
2130
        ParentNode::init(will_query_ranges);
26,781✔
2131
        combine_conditions(!will_query_ranges);
26,781✔
2132

2133
        m_start.clear();
26,781✔
2134
        m_start.resize(m_conditions.size(), 0);
26,781✔
2135

2136
        m_last.clear();
26,781✔
2137
        m_last.resize(m_conditions.size(), 0);
26,781✔
2138

2139
        m_was_match.clear();
26,781✔
2140
        m_was_match.resize(m_conditions.size(), false);
26,781✔
2141

2142
        std::vector<ParentNode*> v;
26,781✔
2143
        for (auto& condition : m_conditions) {
286,101✔
2144
            condition->init(will_query_ranges);
286,101✔
2145
            v.clear();
286,101✔
2146
            condition->gather_children(v);
286,101✔
2147
        }
286,101✔
2148
    }
26,781✔
2149

2150
    size_t find_first_local(size_t start, size_t end) override
2151
    {
3,128,751✔
2152
        if (start >= end)
3,128,751✔
2153
            return not_found;
1,020✔
2154

2155
        size_t index = not_found;
3,127,731✔
2156

2157
        for (size_t c = 0; c < m_conditions.size(); ++c) {
16,774,302✔
2158
            // out of order search; have to discard cached results
2159
            if (start < m_start[c]) {
13,646,571✔
2160
                m_last[c] = 0;
×
2161
                m_was_match[c] = false;
×
2162
            }
×
2163
            // already searched this range and didn't match
2164
            else if (m_last[c] >= end)
13,646,571✔
2165
                continue;
4,301,298✔
2166
            // already search this range and *did* match
2167
            else if (m_was_match[c] && m_last[c] >= start) {
9,345,273✔
2168
                if (index > m_last[c])
5,592,852✔
2169
                    index = m_last[c];
1,111,590✔
2170
                continue;
5,592,852✔
2171
            }
5,592,852✔
2172

2173
            m_start[c] = start;
3,752,421✔
2174
            size_t fmax = std::max(m_last[c], start);
3,752,421✔
2175
            size_t f = m_conditions[c]->find_first(fmax, end);
3,752,421✔
2176
            m_was_match[c] = f != not_found;
3,752,421✔
2177
            m_last[c] = f == not_found ? end : f;
3,752,421✔
2178
            if (f != not_found && index > m_last[c])
3,752,421✔
2179
                index = m_last[c];
2,556,927✔
2180
        }
3,752,421✔
2181

2182
        return index;
3,127,731✔
2183
    }
3,128,751✔
2184

2185
    std::string validate() override
2186
    {
18✔
2187
        if (m_conditions.size() == 0)
18✔
2188
            return "Missing both arguments of OR";
6✔
2189
        if (m_conditions.size() == 1)
12✔
2190
            return "Missing argument of OR";
12✔
2191
        std::string s;
×
2192
        if (m_child != 0)
×
2193
            s = m_child->validate();
×
2194
        if (s != "")
×
2195
            return s;
×
2196
        for (size_t i = 0; i < m_conditions.size(); ++i) {
×
2197
            s = m_conditions[i]->validate();
×
2198
            if (s != "")
×
2199
                return s;
×
2200
        }
×
2201
        return "";
×
2202
    }
×
2203

2204
    std::unique_ptr<ParentNode> clone() const override
2205
    {
27,705✔
2206
        return std::unique_ptr<ParentNode>(new OrNode(*this));
27,705✔
2207
    }
27,705✔
2208

2209
    std::vector<std::unique_ptr<ParentNode>> m_conditions;
2210

2211
private:
2212
    struct ConditionType {
2213
        ConditionType(const ParentNode& node)
2214
            : m_col(node.m_condition_column_key.value)
9,073,644✔
2215
            , m_type(typeid(node))
9,073,644✔
2216
        {
10,505,367✔
2217
        }
10,505,367✔
2218
        int64_t m_col;
2219
        std::type_index m_type;
2220
        bool operator<(const ConditionType& other) const
2221
        {
5,604,078✔
2222
            return this->m_col < other.m_col && this->m_type < other.m_type;
5,604,078✔
2223
        }
5,604,078✔
2224
        bool operator!=(const ConditionType& other) const
2225
        {
256,647✔
2226
            return this->m_col != other.m_col || this->m_type != other.m_type;
256,647✔
2227
        }
256,647✔
2228
    };
2229

2230
    void combine_conditions(bool ignore_indexes)
2231
    {
26,781✔
2232
        // Although ColKey is not unique per table, it is not important to consider
2233
        // the table when sorting here because ParentNode::m_condition_column_key
2234
        // is only a valid ColKey when the node has a direct condition on a column
2235
        // of the table this query is running on. Any link query nodes use a special
2236
        // LinkChain state to store the column path.
2237
        std::sort(m_conditions.begin(), m_conditions.end(), [](auto& a, auto& b) {
5,123,355✔
2238
            return ConditionType(*a) < ConditionType(*b);
5,123,355✔
2239
        });
5,123,355✔
2240

2241
        bool compute_condition_counts = num_conditions_may_need_combination_counts(m_conditions.size());
26,781✔
2242
        util::FlatMap<ConditionType, size_t> condition_type_counts;
26,781✔
2243
        if (compute_condition_counts) {
26,781✔
2244
            for (auto it = m_conditions.begin(); it != m_conditions.end();) {
480✔
2245
                // no need to try to combine anything other than simple nodes that
2246
                // filter directly on a top-level column since the only nodes that
2247
                // support combinations are string/int/uuid/oid <Equal> types
2248
                if (!(*it)->m_condition_column_key) {
240✔
2249
                    ++it;
×
2250
                    continue;
×
2251
                }
×
2252
                ConditionType cur_type(*(*it));
240✔
2253
                auto next = std::upper_bound(it, m_conditions.end(), cur_type,
240✔
2254
                                             [](const ConditionType& a, const std::unique_ptr<ParentNode>& b) {
1,770✔
2255
                                                 return a < ConditionType(*b);
1,770✔
2256
                                             });
1,770✔
2257
                condition_type_counts[cur_type] = next - it;
240✔
2258
                it = next;
240✔
2259
            }
240✔
2260
        }
240✔
2261

2262
        ParentNode* prev = m_conditions.begin()->get();
26,781✔
2263
        std::optional<size_t> cur_type_count;
26,781✔
2264
        if (compute_condition_counts) {
26,781✔
2265
            cur_type_count = condition_type_counts[ConditionType{*prev}];
240✔
2266
        }
240✔
2267
        auto cond = [&](auto& node) {
717,573✔
2268
            if (prev->consume_condition(*node, ignore_indexes, cur_type_count))
717,573✔
2269
                return true;
458,250✔
2270
            prev = &*node;
259,323✔
2271
            if (compute_condition_counts) {
259,323✔
2272
                cur_type_count = condition_type_counts[ConditionType{*prev}];
256,407✔
2273
            }
256,407✔
2274
            return false;
259,323✔
2275
        };
717,573✔
2276
        m_conditions.erase(std::remove_if(m_conditions.begin() + 1, m_conditions.end(), cond), m_conditions.end());
26,781✔
2277
    }
26,781✔
2278

2279
    // start index of the last find for each cond
2280
    std::vector<size_t> m_start;
2281
    // last looked at index of the last find for each cond
2282
    // is a matching index if m_was_match is true
2283
    std::vector<size_t> m_last;
2284
    std::vector<bool> m_was_match;
2285
};
2286

2287

2288
class NotNode : public ParentNode {
2289
public:
2290
    NotNode(std::unique_ptr<ParentNode> condition)
2291
        : m_condition(std::move(condition))
1,101✔
2292
    {
2,202✔
2293
        m_dT = 50.0;
2,202✔
2294
        if (!m_condition) {
2,202✔
2295
            throw query_parser::InvalidQueryError("Missing argument to Not");
×
2296
        }
×
2297
    }
2,202✔
2298

2299
    void table_changed() override
2300
    {
2,244✔
2301
        m_condition->set_table(m_table);
2,244✔
2302
    }
2,244✔
2303

2304
    void cluster_changed() override
2305
    {
2,730✔
2306
        m_condition->set_cluster(m_cluster);
2,730✔
2307
        // Heuristics bookkeeping:
2308
        m_known_range_start = 0;
2,730✔
2309
        m_known_range_end = 0;
2,730✔
2310
        m_first_in_known_range = not_found;
2,730✔
2311
    }
2,730✔
2312

2313
    void init(bool will_query_ranges) override
2314
    {
2,280✔
2315
        ParentNode::init(will_query_ranges);
2,280✔
2316
        std::vector<ParentNode*> v;
2,280✔
2317

2318
        m_condition->init(false);
2,280✔
2319
        v.clear();
2,280✔
2320
        m_condition->gather_children(v);
2,280✔
2321
    }
2,280✔
2322

2323
    size_t find_first_local(size_t start, size_t end) override;
2324

2325
    std::string describe(util::serializer::SerialisationState& state) const override
2326
    {
924✔
2327
        if (m_condition) {
924✔
2328
            return "!(" + m_condition->describe_expression(state) + ")";
924✔
2329
        }
924✔
2330
        return "!()";
×
2331
    }
924✔
2332

2333
    void collect_dependencies(std::vector<TableKey>& versions) const override
2334
    {
48✔
2335
        if (m_condition) {
48✔
2336
            m_condition->collect_dependencies(versions);
48✔
2337
        }
48✔
2338
    }
48✔
2339

2340
    std::unique_ptr<ParentNode> clone() const override
2341
    {
4,080✔
2342
        return std::unique_ptr<ParentNode>(new NotNode(*this));
4,080✔
2343
    }
4,080✔
2344

2345
    NotNode(const NotNode& from)
2346
        : ParentNode(from)
2,040✔
2347
        , m_condition(from.m_condition ? from.m_condition->clone() : nullptr)
2,040✔
2348
        , m_known_range_start(from.m_known_range_start)
2,040✔
2349
        , m_known_range_end(from.m_known_range_end)
2,040✔
2350
        , m_first_in_known_range(from.m_first_in_known_range)
2,040✔
2351
    {
4,080✔
2352
    }
4,080✔
2353

2354
    std::unique_ptr<ParentNode> m_condition;
2355

2356
private:
2357
    // FIXME This heuristic might as well be reused for all condition nodes.
2358
    size_t m_known_range_start;
2359
    size_t m_known_range_end;
2360
    size_t m_first_in_known_range;
2361

2362
    bool evaluate_at(size_t rowndx);
2363
    void update_known(size_t start, size_t end, size_t first);
2364
    size_t find_first_loop(size_t start, size_t end);
2365
    size_t find_first_covers_known(size_t start, size_t end);
2366
    size_t find_first_covered_by_known(size_t start, size_t end);
2367
    size_t find_first_overlap_lower(size_t start, size_t end);
2368
    size_t find_first_overlap_upper(size_t start, size_t end);
2369
    size_t find_first_no_overlap(size_t start, size_t end);
2370
};
2371

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

2389
    void table_changed() override
2390
    {
26,808✔
2391
        if (m_table) {
26,808✔
2392
            ParentNode::m_table->check_column(m_condition_column_key1);
26,808✔
2393
            ParentNode::m_table->check_column(m_condition_column_key2);
26,808✔
2394
        }
26,808✔
2395
    }
26,808✔
2396

2397
    static std::unique_ptr<ArrayPayload> update_cached_leaf_pointers_for_column(Allocator& alloc,
2398
                                                                                const ColKey& col_key);
2399
    void cluster_changed() override
2400
    {
30,909✔
2401
        if (!m_leaf1) {
30,909✔
2402
            m_leaf1 =
29,124✔
2403
                update_cached_leaf_pointers_for_column(m_table.unchecked_ptr()->get_alloc(), m_condition_column_key1);
29,124✔
2404
        }
29,124✔
2405
        if (!m_leaf2) {
30,909✔
2406
            m_leaf2 =
29,124✔
2407
                update_cached_leaf_pointers_for_column(m_table.unchecked_ptr()->get_alloc(), m_condition_column_key2);
29,124✔
2408
        }
29,124✔
2409
        m_cluster->init_leaf(m_condition_column_key1, m_leaf1.get());
30,909✔
2410
        m_cluster->init_leaf(m_condition_column_key2, m_leaf2.get());
30,909✔
2411
    }
30,909✔
2412

2413
    std::string describe(util::serializer::SerialisationState& state) const override
2414
    {
×
2415
        REALM_ASSERT(m_condition_column_key1 && m_condition_column_key2);
×
2416
        return state.describe_column(ParentNode::m_table, m_condition_column_key1) + " " + describe_condition() +
×
2417
               " " + state.describe_column(ParentNode::m_table, m_condition_column_key2);
×
2418
    }
×
2419

2420
    TwoColumnsNodeBase(const TwoColumnsNodeBase& from)
2421
        : ParentNode(from)
10,914✔
2422
        , m_condition_column_key1(from.m_condition_column_key1)
10,914✔
2423
        , m_condition_column_key2(from.m_condition_column_key2)
10,914✔
2424
    {
21,828✔
2425
    }
21,828✔
2426

2427
protected:
2428
    ColKey m_condition_column_key1;
2429
    ColKey m_condition_column_key2;
2430
    std::unique_ptr<ArrayPayload> m_leaf1;
2431
    std::unique_ptr<ArrayPayload> m_leaf2;
2432
};
2433

2434

2435
template <class TConditionFunction>
2436
class TwoColumnsNode : public TwoColumnsNodeBase {
2437
public:
2438
    using TwoColumnsNodeBase::TwoColumnsNodeBase;
2439
    size_t find_first_local(size_t start, size_t end) override
2440
    {
291,255✔
2441
        size_t s = start;
291,255✔
2442
        while (s < end) {
763,164✔
2443
            QueryValue v1(m_leaf1->get_any(s));
719,664✔
2444
            QueryValue v2(m_leaf2->get_any(s));
719,664✔
2445
            if (TConditionFunction()(v1, v2))
719,664✔
2446
                return s;
247,755✔
2447
            else
471,909✔
2448
                s++;
471,909✔
2449
        }
719,664✔
2450
        return not_found;
43,500✔
2451
    }
291,255✔
2452

2453
    std::string describe_condition() const override
2454
    {
×
2455
        return TConditionFunction::description();
×
2456
    }
×
2457

2458
    std::unique_ptr<ParentNode> clone() const override
2459
    {
21,828✔
2460
        return std::unique_ptr<ParentNode>(new TwoColumnsNode<TConditionFunction>(*this));
21,828✔
2461
    }
21,828✔
2462
};
2463

2464

2465
// For Next-Generation expressions like col1 / col2 + 123 > col4 * 100.
2466
class ExpressionNode : public ParentNode {
2467
public:
2468
    ExpressionNode(std::unique_ptr<Expression>);
2469

2470
    void init(bool) override;
2471
    size_t find_first_local(size_t start, size_t end) override;
2472

2473
    void table_changed() override;
2474
    void cluster_changed() override;
2475
    void collect_dependencies(std::vector<TableKey>&) const override;
2476

2477
    std::string describe(util::serializer::SerialisationState& state) const override;
2478

2479
    std::unique_ptr<ParentNode> clone() const override;
2480

2481
private:
2482
    ExpressionNode(const ExpressionNode& from);
2483

2484
    std::unique_ptr<Expression> m_expression;
2485
};
2486

2487

2488
class LinksToNodeBase : public ParentNode {
2489
public:
2490
    LinksToNodeBase(ColKey origin_column_key, ObjKey target_key)
2491
        : LinksToNodeBase(origin_column_key, std::vector<ObjKey>{target_key})
6,810✔
2492
    {
13,620✔
2493
    }
13,620✔
2494

2495
    LinksToNodeBase(ColKey origin_column_key, const std::vector<ObjKey>& target_keys)
2496
        : m_target_keys(target_keys)
7,044✔
2497
    {
14,088✔
2498
        m_dT = 50.0;
14,088✔
2499
        m_condition_column_key = origin_column_key;
14,088✔
2500
        auto column_type = origin_column_key.get_type();
14,088✔
2501
        REALM_ASSERT(column_type == col_type_Link);
14,088✔
2502
        REALM_ASSERT(!m_target_keys.empty());
14,088✔
2503
    }
14,088✔
2504

2505
    void cluster_changed() override
2506
    {
50,928✔
2507
        if (m_condition_column_key.is_collection()) {
50,928✔
2508
            m_linklist.emplace(m_table.unchecked_ptr()->get_alloc());
24,924✔
2509
            m_leaf = &*m_linklist;
24,924✔
2510
        }
24,924✔
2511
        else {
26,004✔
2512
            m_list.emplace(m_table.unchecked_ptr()->get_alloc());
26,004✔
2513
            m_leaf = &*m_list;
26,004✔
2514
        }
26,004✔
2515
        m_cluster->init_leaf(this->m_condition_column_key, m_leaf);
50,928✔
2516
    }
50,928✔
2517

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

2538
protected:
2539
    std::vector<ObjKey> m_target_keys;
2540
    std::optional<ArrayKey> m_list;
2541
    std::optional<ArrayList> m_linklist;
2542
    ArrayPayload* m_leaf = nullptr;
2543

2544
    LinksToNodeBase(const LinksToNodeBase& source)
2545
        : ParentNode(source)
7,686✔
2546
        , m_target_keys(source.m_target_keys)
7,686✔
2547
    {
15,372✔
2548
    }
15,372✔
2549

2550
    ref_type get_ref(size_t i)
2551
    {
6,025,644✔
2552
        if (m_list)
6,025,644✔
2553
            return m_list->get_as_ref(i);
×
2554
        return m_linklist->get(i);
6,025,644✔
2555
    }
6,025,644✔
2556
};
2557

2558
template <class TConditionFunction>
2559
class LinksToNode : public LinksToNodeBase {
2560
public:
2561
    using LinksToNodeBase::LinksToNodeBase;
2562

2563
    std::string describe_condition() const override
2564
    {
252✔
2565
        return TConditionFunction::description();
252✔
2566
    }
252✔
2567

2568
    std::unique_ptr<ParentNode> clone() const override
2569
    {
15,372✔
2570
        return std::unique_ptr<ParentNode>(new LinksToNode<TConditionFunction>(*this));
15,372✔
2571
    }
15,372✔
2572

2573
    size_t find_first_local(size_t start, size_t end) override;
2574
};
2575

2576
} // namespace realm
2577

2578
#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