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

realm / realm-core / jonathan.reams_2947

01 Dec 2023 08:08PM UTC coverage: 91.739% (+0.04%) from 91.695%
jonathan.reams_2947

Pull #7160

Evergreen

jbreams
allow handle_error to decide resumability
Pull Request #7160: Prevent resuming a session that has not been fully shut down

92428 of 169414 branches covered (0.0%)

315 of 349 new or added lines in 14 files covered. (90.26%)

80 existing lines in 14 files now uncovered.

232137 of 253041 relevant lines covered (91.74%)

6882826.18 hits per line

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

93.59
/src/realm/query_expression.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
This file lets you write queries in C++ syntax like: Expression* e = (first + 1 / second >= third + 12.3);
21

22
Type conversion/promotion semantics is the same as in the C++ expressions, e.g float + int > double == float +
23
(float)int > double.
24

25

26
Grammar:
27
-----------------------------------------------------------------------------------------------------------------------
28
    Expression:         Subexpr2<T>  Compare<Cond, T>  Subexpr2<T>
29
                        operator! Expression
30

31
    Subexpr2<T>:        Value<T>
32
                        Columns<T>
33
                        Subexpr2<T>  Operator<Oper<T>  Subexpr2<T>
34
                        power(Subexpr2<T>) // power(x) = x * x, as example of unary operator
35

36
    Value<T>:           T
37

38
    Operator<Oper<T>>:  +, -, *, /
39

40
    Compare<Cond, T>:   ==, !=, >=, <=, >, <
41

42
    T:                  bool, int, int64_t, float, double, StringData
43

44

45
Class diagram
46
-----------------------------------------------------------------------------------------------------------------------
47
Subexpr2
48
    void evaluate(size_t i, ValueBase* destination)
49

50
Compare: public Subexpr2
51
    size_t find_first(size_t start, size_t end)     // main method that executes query
52

53
    unique_ptr<Subexpr2> m_left;                               // left expression subtree
54
    unique_ptr<Subexpr2> m_right;                              // right expression subtree
55

56
Operator: public Subexpr2
57
    void evaluate(size_t i, ValueBase* destination)
58
    unique_ptr<Subexpr2> m_left;                               // left expression subtree
59
    unique_ptr<Subexpr2> m_right;                              // right expression subtree
60

61
Value<T>: public Subexpr2
62
    void evaluate(size_t i, ValueBase* destination)
63
    T m_v[8];
64

65
Columns<T>: public Subexpr2
66
    void evaluate(size_t i, ValueBase* destination)
67
    SequentialGetter<T> sg;                         // class bound to a column, lets you read values in a fast way
68
    Table* m_table;
69

70
class ColumnAccessor<>: public Columns<double>
71

72

73
Call diagram:
74
-----------------------------------------------------------------------------------------------------------------------
75
Example of 'table.first > 34.6 + table.second':
76

77
size_t Compare<Greater>::find_first()-------------+
78
         |                                        |
79
         |                                        |
80
         |                                        |
81
         +--> Columns<float>::evaluate()          +--------> Operator<Plus>::evaluate()
82
                                                                |               |
83
                                               Value<float>::evaluate()    Columns<float>::evaluate()
84

85
Operator, Value and Columns have an evaluate(size_t i, ValueBase* destination) method which returns a Value<T>
86
containing 8 values representing table rows i...i + 7.
87

88
So Value<T> contains 8 concecutive values and all operations are based on these chunks. This is
89
to save overhead by virtual calls needed for evaluating a query that has been dynamically constructed at runtime.
90

91

92
Memory allocation:
93
-----------------------------------------------------------------------------------------------------------------------
94
Subexpressions created by the end-user are stack allocated. They are cloned to the heap when passed to UnaryOperator,
95
Operator, and Compare. Those types own the clones and deallocate them when destroyed.
96

97

98
Caveats, notes and todos
99
-----------------------------------------------------------------------------------------------------------------------
100
    * Perhaps disallow columns from two different tables in same expression
101
    * The name Columns (with s) an be confusing because we also have Column (without s)
102
    * We have Columns::m_table, Query::m_table and ColumnAccessorBase::m_table that point at the same thing, even with
103
      ColumnAccessor<> extending Columns. So m_table is redundant, but this is in order to keep class dependencies and
104
      entanglement low so that the design is flexible (if you perhaps later want a Columns class that is not dependent
105
      on ColumnAccessor)
106

107
Nulls
108
-----------------------------------------------------------------------------------------------------------------------
109
First note that at array level, nulls are distinguished between non-null in different ways:
110
String:
111
    m_data == 0 && m_size == 0
112

113
Integer, Bool stored in ArrayIntNull:
114
    value == get(0) (entry 0 determins a magic value that represents nulls)
115

116
Float/double:
117
    null::is_null(value) which tests if value bit-matches one specific bit pattern reserved for null
118

119
The Columns class encapsulates all this into a simple class that, for any type T has
120
    evaluate(size_t index) that reads values from a column, taking nulls in count
121
    get(index)
122
    set(index)
123
    is_null(index)
124
    set_null(index)
125
*/
126

127
#ifndef REALM_QUERY_EXPRESSION_HPP
128
#define REALM_QUERY_EXPRESSION_HPP
129

130
#include <realm/aggregate_ops.hpp>
131
#include <realm/array_timestamp.hpp>
132
#include <realm/array_binary.hpp>
133
#include <realm/array_string.hpp>
134
#include <realm/array_backlink.hpp>
135
#include <realm/array_list.hpp>
136
#include <realm/array_key.hpp>
137
#include <realm/array_bool.hpp>
138
#include <realm/array_fixed_bytes.hpp>
139
#include <realm/column_integer.hpp>
140
#include <realm/column_type_traits.hpp>
141
#include <realm/dictionary.hpp>
142
#if REALM_ENABLE_GEOSPATIAL
143
#include <realm/geospatial.hpp>
144
#endif
145
#include <realm/table.hpp>
146
#include <realm/index_string.hpp>
147
#include <realm/query.hpp>
148
#include <realm/list.hpp>
149
#include <realm/set.hpp>
150
#include <realm/query_value.hpp>
151
#include <realm/util/optional.hpp>
152
#include <realm/util/serializer.hpp>
153

154
#include <external/mpark/variant.hpp>
155

156
#include <numeric>
157
#include <algorithm>
158

159
// Normally, if a next-generation-syntax condition is supported by the old query_engine.hpp, a query_engine node is
160
// created because it's faster (by a factor of 5 - 10). Because many of our existing next-generation-syntax unit
161
// unit tests are indeed simple enough to fallback to old query_engine, query_expression gets low test coverage. Undef
162
// flag to get higher query_expression test coverage. This is a good idea to try out each time you develop on/modify
163
// query_expression.
164

165
#define REALM_OLDQUERY_FALLBACK 1
63,988✔
166

167
namespace realm {
168

169
template <class T>
170
T minimum(T a, T b)
171
{
3,346,314✔
172
    return a < b ? a : b;
3,181,152✔
173
}
3,346,314✔
174

175
struct Plus {
176
    Mixed operator()(Mixed v1, Mixed v2) const
177
    {
5,390,952✔
178
        return v1 + v2;
5,390,952✔
179
    }
5,390,952✔
180
    static std::string description()
181
    {
36✔
182
        return "+";
36✔
183
    }
36✔
184
};
185

186
struct Minus {
187
    Mixed operator()(Mixed v1, Mixed v2) const
188
    {
2,815,064✔
189
        return v1 - v2;
2,815,064✔
190
    }
2,815,064✔
191
    static std::string description()
192
    {
16✔
193
        return "-";
16✔
194
    }
16✔
195
};
196

197
struct Div {
198
    Mixed operator()(Mixed v1, Mixed v2) const
199
    {
1,127,130✔
200
        return v1 / v2;
1,127,130✔
201
    }
1,127,130✔
202
    static std::string description()
203
    {
68✔
204
        return "/";
68✔
205
    }
68✔
206
};
207

208
struct Mul {
209
    Mixed operator()(Mixed v1, Mixed v2) const
210
    {
1,127,934✔
211
        return v1 * v2;
1,127,934✔
212
    }
1,127,934✔
213
    static std::string description()
214
    {
84✔
215
        return "*";
84✔
216
    }
84✔
217
};
218

219
// Finds a common type for T1 and T2 according to C++ conversion/promotion in arithmetic (float + int => float, etc)
220
template <class T1, class T2, bool T1_is_int = std::numeric_limits<T1>::is_integer || std::is_same_v<T1, null>,
221
          bool T2_is_int = std::numeric_limits<T2>::is_integer || std::is_same_v<T2, null>,
222
          bool T1_is_widest = (sizeof(T1) > sizeof(T2) || std::is_same_v<T2, null>)>
223
struct Common;
224
template <class T1, class T2, bool b>
225
struct Common<T1, T2, b, b, true> {
226
    typedef T1 type;
227
};
228
template <class T1, class T2, bool b>
229
struct Common<T1, T2, b, b, false> {
230
    typedef T2 type;
231
};
232
template <class T1, class T2, bool b>
233
struct Common<T1, T2, false, true, b> {
234
    typedef T1 type;
235
};
236
template <class T1, class T2, bool b>
237
struct Common<T1, T2, true, false, b> {
238
    typedef T2 type;
239
};
240

241
class ValueBase {
242
public:
243
    using ValueType = QueryValue;
244

245
    static constexpr size_t chunk_size = 8;
246
    bool m_from_list = false;
247

248
    ValueBase() = default;
11,673,681✔
249
    ValueBase(const ValueType& init_val)
250
    {
744,618✔
251
        m_first[0] = init_val;
744,618✔
252
    }
744,618✔
253
    ~ValueBase()
254
    {
12,466,860✔
255
        dealloc();
12,466,860✔
256
    }
12,466,860✔
257
    ValueBase(const ValueBase& other)
258
    {
44,142✔
259
        *this = other;
44,142✔
260
    }
44,142✔
261

262
    ValueBase& operator=(const ValueBase& other)
263
    {
2,063,304✔
264
        m_from_list = other.m_from_list;
2,063,304✔
265
        set(other.begin(), other.end());
2,063,304✔
266
        return *this;
2,063,304✔
267
    }
2,063,304✔
268

269
    size_t size() const
270
    {
21,275,466✔
271
        return m_size;
21,275,466✔
272
    }
21,275,466✔
273

274
    void init(bool from_link_list, size_t nb_values)
275
    {
6,690,531✔
276
        m_from_list = from_link_list;
6,690,531✔
277
        m_sorted = false;
6,690,531✔
278
        resize(nb_values);
6,690,531✔
279
    }
6,690,531✔
280

281
    void init_for_links(bool only_unary_links, size_t size)
282
    {
1,886,418✔
283
        if (only_unary_links) {
1,886,418✔
284
            REALM_ASSERT(size <= 1);
1,811,748✔
285
            init(false, 1);
1,811,748✔
286
            set_null(0);
1,811,748✔
287
        }
1,811,748✔
288
        else {
74,670✔
289
            init(true, size);
74,670✔
290
        }
74,670✔
291
    }
1,886,418✔
292

293
    void set_null(size_t ndx)
294
    {
3,256,791✔
295
        m_first[ndx] = ValueType();
3,256,791✔
296
    }
3,256,791✔
297

298
    template <class T>
299
    void set(size_t ndx, const T& val)
300
    {
51,606,239✔
301
        if constexpr (std::is_same<T, float>::value || std::is_same<T, double>::value) {
51,606,239✔
302
            m_first[ndx] = null::is_null_float(val) ? ValueType() : ValueType(val);
34,219,415✔
303
        }
17,735,121✔
304
        else {
50,357,959✔
305
            m_first[ndx] = ValueType(val);
50,357,959✔
306
        }
50,357,959✔
307
    }
51,606,239✔
308

309
    template <class T>
310
    void set(T b, T e)
311
    {
3,419,546✔
312
        size_t sz = e - b;
3,419,546✔
313
        resize(sz);
3,419,546✔
314
        size_t i = 0;
3,419,546✔
315
        for (auto from = b; from != e; ++from) {
29,080,442✔
316
            set(i, *from);
25,660,896✔
317
            i++;
25,660,896✔
318
        }
25,660,896✔
319
    }
3,419,546✔
320

321
    ValueType& operator[](size_t n)
322
    {
10,439,589✔
323
        return m_first[n];
10,439,589✔
324
    }
10,439,589✔
325

326
    const ValueType& operator[](size_t n) const
327
    {
17,540,768✔
328
        return m_first[n];
17,540,768✔
329
    }
17,540,768✔
330

331
    const ValueType& get(size_t n) const
332
    {
747,603✔
333
        return m_first[n];
747,603✔
334
    }
747,603✔
335

336
    ValueType* begin()
337
    {
171,966✔
338
        return m_first;
171,966✔
339
    }
171,966✔
340
    const ValueType* begin() const
341
    {
2,063,730✔
342
        return m_first;
2,063,730✔
343
    }
2,063,730✔
344

345
    ValueType* end()
346
    {
133,284✔
347
        return m_first + m_size;
133,284✔
348
    }
133,284✔
349
    const ValueType* end() const
350
    {
2,063,730✔
351
        return m_first + m_size;
2,063,730✔
352
    }
2,063,730✔
353
    void sort()
354
    {
1,092✔
355
        if (!m_sorted) {
1,092✔
356
            std::sort(begin(), end());
924✔
357
            m_sorted = true;
924✔
358
        }
924✔
359
    }
1,092✔
360
    template <class TOperator>
361
    REALM_FORCEINLINE void fun_const(const ValueType& const_value, const ValueBase& right)
362
    {
488✔
363
        TOperator o;
488✔
364
        // Operate on values one-by-one
244✔
365
        size_t sz = right.size();
488✔
366
        init(right.m_from_list, sz);
488✔
367
        for (size_t i = 0; i < sz; i++) {
1,560✔
368
            set(i, o(const_value, right[i]));
1,072✔
369
        }
1,072✔
370
    }
488✔
371
    template <class TOperator>
372
    REALM_FORCEINLINE void fun_const(const ValueBase& left, const ValueType& const_value)
373
    {
428,376✔
374
        TOperator o;
428,376✔
375
        // Operate on values one-by-one
216,630✔
376
        size_t sz = left.size();
428,376✔
377
        init(left.m_from_list, sz);
428,376✔
378
        for (size_t i = 0; i < sz; i++) {
3,808,656✔
379
            set(i, o(left[i], const_value));
3,380,280✔
380
        }
3,380,280✔
381
    }
428,376✔
382
    template <class TOperator>
383
    REALM_FORCEINLINE void fun(const ValueBase& left, const ValueBase& right)
384
    {
896,660✔
385
        TOperator o;
896,660✔
386

453,214✔
387
        if (!left.m_from_list && !right.m_from_list) {
896,660✔
388
            // Operate on values one-by-one (one value is one row; no links)
453,212✔
389
            size_t min = std::min(left.size(), right.size());
896,656✔
390
            init(false, min);
896,656✔
391

453,212✔
392
            for (size_t i = 0; i < min; i++) {
7,976,392✔
393
                set(i, o(left[i], right[i]));
7,079,736✔
394
            }
7,079,736✔
395
        }
896,656✔
396
        else if (left.m_from_list && right.m_from_list) {
4!
397
            // FIXME: Many-to-many links not supported yet. Need to specify behaviour
2✔
398
            // Eg: `{1, 2, 3} * {4, 5} > age`
2✔
399
            throw LogicError(ErrorCodes::InvalidQuery, "Operations involving two lists are not supported");
4✔
400
        }
4✔
UNCOV
401
        else if (!left.m_from_list && right.m_from_list) {
×
402
            // Right values come from link. Left must come from single row.
403
            REALM_ASSERT_DEBUG(left.size() > 0);
×
404
            init(true, right.size());
×
405

406
            auto left_value = left[0];
×
407
            for (size_t i = 0; i < right.size(); i++) {
×
408
                set(i, o(left_value, right[i]));
×
409
            }
×
410
        }
×
UNCOV
411
        else if (left.m_from_list && !right.m_from_list) {
×
412
            // Same as above, but with left values coming from links
413
            REALM_ASSERT_DEBUG(right.size() > 0);
×
414
            init(true, left.size());
×
415

416
            auto right_value = right[0];
×
417
            for (size_t i = 0; i < left.size(); i++) {
×
418
                set(i, o(left[i], right_value));
×
419
            }
×
420
        }
×
421
    }
896,660✔
422

423
    // Given a TCond (==, !=, >, <, >=, <=) and two Value<T>, return index of first match
424
    template <class TCond>
425
    REALM_FORCEINLINE static size_t compare(ValueBase& left, ValueBase& right,
426
                                            util::Optional<ExpressionComparisonType> left_cmp_type,
427
                                            util::Optional<ExpressionComparisonType> right_cmp_type)
428
    {
3,427,643✔
429
        TCond c;
3,427,643✔
430
        using Compare = ExpressionComparisonType;
3,427,643✔
431

1,714,535✔
432
        if (!left.m_from_list && !right.m_from_list) {
3,427,643✔
433
            // ALL/NONE not supported for non list types
1,592,398✔
434
            REALM_ASSERT_DEBUG(!left_cmp_type || *left_cmp_type == Compare::Any);
3,183,210!
435
            REALM_ASSERT_DEBUG(!right_cmp_type || *right_cmp_type == Compare::Any);
3,183,210!
436
            // Compare values one-by-one (one value is one row; no link lists)
1,592,398✔
437
            size_t min = minimum(left.size(), right.size());
3,183,210✔
438
            for (size_t m = 0; m < min; m++) {
5,213,283✔
439
                if (c(left[m], right[m]))
3,480,137✔
440
                    return m;
1,450,064✔
441
            }
3,480,137✔
442
            return not_found;
2,456,857✔
443
        }
244,433✔
444

122,137✔
445
        if (left.m_from_list && right.m_from_list && !left_cmp_type && !right_cmp_type) {
244,433!
446
            // Both lists and no ANY, NONE, ALL specified - simple element by element comparison
1,886✔
447
            if (left.size() != right.size()) {
3,772!
448
                if constexpr (std::is_same_v<TCond, NotEqual>) {
2,400✔
449
                    return 0; // mismatch size
2,148✔
450
                }
2,148✔
451
                else {
2,148✔
452
                    return not_found;
2,148✔
453
                }
2,148✔
454
            }
1,264✔
455
            for (size_t i = 0; i < left.size(); ++i) {
3,372!
456
                if (!c(left[i], right[i])) {
2,864!
457
                    return not_found;
864✔
458
                }
864✔
459
            }
2,864✔
460
            return 0; // all elements matched in the right order
940✔
461
        }
240,661✔
462

120,251✔
463
        // if one side omitted a comparison type, assume ANY
120,251✔
464
        const Compare compare_left = left_cmp_type.value_or(Compare::Any);
240,661✔
465
        const Compare compare_right = right_cmp_type.value_or(Compare::Any);
240,661✔
466

120,251✔
467
        size_t left_size = left.m_from_list ? left.size() : 1;
186,377✔
468
        size_t right_size = right.m_from_list ? right.size() : 1;
188,161✔
469

120,251✔
470
        if (left_size > 2 && right_size > 2) {
240,661!
471
            left.sort();
476✔
472
            right.sort();
476✔
473

238✔
474
            if constexpr (std::is_same_v<TCond, Equal>) {
476✔
475
                if (compare_left != ExpressionComparisonType::None && compare_right == Compare::Any) {
336✔
476
                    // Optimization with O(n) complexity
63✔
477
                    const bool any = compare_left == ExpressionComparisonType::Any;
126✔
478
                    size_t left_idx = 0;
126✔
479
                    size_t right_idx = 0;
126✔
480
                    while (right_idx < right_size) {
1,002✔
481
                        if (c(left[left_idx], right[right_idx])) {
966✔
482
                            left_idx++;
216✔
483
                            right_idx++;
216✔
484
                            if (any || left_idx == left_size) {
216✔
485
                                return 0;
48✔
486
                            }
48✔
487
                        }
750✔
488
                        else {
750✔
489
                            if (left[left_idx] < right[right_idx]) {
750✔
490
                                if (any && left_idx < left_size - 1) {
414✔
491
                                    left_idx++;
372✔
492
                                }
372✔
493
                                else {
42✔
494
                                    return not_found;
42✔
495
                                }
42✔
496
                            }
336✔
497
                            else {
336✔
498
                                right_idx++;
336✔
499
                            }
336✔
500
                        }
750✔
501
                    }
966✔
502
                    return not_found;
81✔
503
                }
173,736✔
504
            }
336✔
505
            else if constexpr (realm::is_any_v<TCond, Greater, GreaterEqual, Less, LessEqual>) {
140✔
506
                // Only consider first and last
507
                left[1] = left[left_size - 1];
70✔
508
                left_size = 2;
70✔
509
                right[1] = right[right_size - 1];
70✔
510
                right_size = 2;
70✔
511
            }
70✔
512
            else {
×
513
                // remove duplicates to reduce comparison time in nested loops
514
                left_size = std::unique(left.begin(), left.end()) - left.begin();
×
515
                right_size = std::unique(right.begin(), right.end()) - right.begin();
×
516
            }
×
517
        }
476✔
518

86,787✔
519
        if constexpr (realm::is_any_v<TCond, BeginsWith, BeginsWithIns, EndsWith, EndsWithIns, Contains, ContainsIns,
240,535✔
520
                                      Like, LikeIns>) {
120,188✔
521
            // The string operators have the arguments reversed so we have to iterate right in the
112,070✔
522
            // outer loop as this is actually the left argument
112,070✔
523
            auto left_matches = [&](const QueryValue& right_val) {
123,161✔
524
                for (size_t i = 0; i < left_size; i++) {
40,338✔
525
                    if (c(left[i], right_val)) {
23,142✔
526
                        // match
2,385✔
527
                        if (compare_left == Compare::Any) {
4,770✔
528
                            return true;
4,542✔
529
                        }
4,542✔
530
                        if (compare_left == Compare::None) {
228!
531
                            return false; // one matched
60✔
532
                        }
60✔
533
                    }
18,372✔
534
                    else {
18,372✔
535
                        // no match
9,186✔
536
                        if (compare_left == Compare::All) {
18,372✔
537
                            return false;
384✔
538
                        }
384✔
539
                    }
18,372✔
540
                }
23,142✔
541
                return compare_left == Compare::None || compare_left == Compare::All;
19,689✔
542
            };
22,182✔
543

112,070✔
544
            for (size_t i = 0; i < right_size; i++) {
128,705✔
545
                if (left_matches(right[i])) {
22,182✔
546
                    if (compare_right == Compare::Any) {
4,722✔
547
                        return 0;
2,778✔
548
                    }
2,778✔
549
                    if (compare_right == Compare::None) {
1,944✔
550
                        return not_found; // one matched
1,068✔
551
                    }
1,068✔
552
                }
17,460✔
553
                else {
17,460✔
554
                    if (compare_right == Compare::All) {
17,460✔
555
                        return not_found;
1,308✔
556
                    }
1,308✔
557
                }
17,460✔
558
            }
22,182✔
559
            if (compare_right == Compare::None || compare_right == Compare::All) {
117,614✔
560
                return 0; // either none or all
2,088✔
561
            }
2,088✔
562
        }
8,997✔
563
        else {
224,296✔
564
            auto right_matches = [&](const QueryValue& left_val) {
258,041✔
565
                for (size_t i = 0; i < right_size; i++) {
480,371✔
566
                    if (c(left_val, right[i])) {
347,064✔
567
                        // match
63,114✔
568
                        if (compare_right == Compare::Any) {
126,228!
569
                            return true;
123,802✔
570
                        }
123,802✔
571
                        if (compare_right == Compare::None) {
2,426✔
572
                            return false; // one matched
278✔
573
                        }
278✔
574
                    }
220,836✔
575
                    else {
220,836✔
576
                        // no match
110,421✔
577
                        if (compare_right == Compare::All) {
220,836✔
578
                            return false;
654✔
579
                        }
654✔
580
                    }
220,836✔
581
                }
347,064✔
582
                return compare_right == Compare::None || compare_right == Compare::All;
195,674✔
583
            };
258,041✔
584

112,070✔
585
            for (size_t i = 0; i < left_size; i++) {
360,765✔
586
                if (right_matches(left[i])) {
258,041✔
587
                    if (compare_left == Compare::Any) {
124,626✔
588
                        return 0;
100,322✔
589
                    }
100,322✔
590
                    if (compare_left == ExpressionComparisonType::None) {
24,304✔
591
                        return not_found; // one matched
10,080✔
592
                    }
10,080✔
593
                }
133,415✔
594
                else {
133,415✔
595
                    if (compare_left == ExpressionComparisonType::All) {
133,415✔
596
                        return not_found;
11,170✔
597
                    }
11,170✔
598
                }
133,415✔
599
            }
258,041✔
600
            if (compare_left == ExpressionComparisonType::None || compare_left == ExpressionComparisonType::All) {
163,510!
601
                return 0; // either none or all
23,490✔
602
            }
23,490✔
603
        }
79,234✔
604

39,539✔
605
        return not_found; // no match
88,231✔
606
    }
88,231✔
607

608
private:
609
    // If true, all values in the class come from a link list of a single field in the parent table (m_table). If
610
    // false, then values come from successive rows of m_table (query operations are operated on in bulks for speed)
611
    static constexpr size_t prealloc = 8;
612

613
    QueryValue m_cache[prealloc];
614
    QueryValue* m_first = &m_cache[0];
615
    size_t m_size = 1;
616
    bool m_sorted = false;
617

618
    void resize(size_t size)
619
    {
10,747,053✔
620
        if (size == m_size)
10,747,053✔
621
            return;
4,719,585✔
622

3,040,458✔
623
        dealloc();
6,027,468✔
624
        m_size = size;
6,027,468✔
625
        if (m_size > 0) {
6,027,468✔
626
            if (m_size > prealloc)
6,009,768✔
627
                m_first = new QueryValue[m_size];
15,012✔
628
            else
5,994,756✔
629
                m_first = &m_cache[0];
5,994,756✔
630
        }
6,009,768✔
631
    }
6,027,468✔
632
    void dealloc()
633
    {
18,493,929✔
634
        if (m_first) {
18,493,929✔
635
            if (m_size > prealloc)
18,467,217✔
636
                delete[] m_first;
15,012✔
637
            m_first = nullptr;
18,467,217✔
638
        }
18,467,217✔
639
    }
18,493,929✔
640
    void fill(const QueryValue& val)
641
    {
×
642
        for (size_t i = 0; i < m_size; i++) {
×
643
            m_first[i] = val;
×
644
        }
×
645
    }
×
646
};
647

648
class Expression {
649
public:
650
    virtual ~Expression() = default;
123,345✔
651

652
    virtual double init()
653
    {
1,698✔
654
        return 50.0; // Default dT
1,698✔
655
    }
1,698✔
656

657
    virtual size_t find_first(size_t start, size_t end) const = 0;
658
    virtual void set_base_table(ConstTableRef table) = 0;
659
    virtual void set_cluster(const Cluster*) = 0;
660
    virtual void collect_dependencies(std::vector<TableKey>&) const {}
288✔
661
    virtual ConstTableRef get_base_table() const = 0;
662
    virtual std::string description(util::serializer::SerialisationState& state) const = 0;
663

664
    virtual std::unique_ptr<Expression> clone() const = 0;
665
};
666

667
template <typename T, typename... Args>
668
std::unique_ptr<Expression> make_expression(Args&&... args)
669
{
25,404✔
670
    return std::unique_ptr<Expression>(new T(std::forward<Args>(args)...));
25,404✔
671
}
25,404✔
672

673
class Subexpr {
674
public:
675
    virtual ~Subexpr() = default;
10,013,049✔
676

677
    virtual std::unique_ptr<Subexpr> clone() const = 0;
678

679
    // When the user constructs a query, it always "belongs" to one single base/parent table (regardless of
680
    // any links or not and regardless of any queries assembled with || or &&). When you do a Query::find(),
681
    // then Query::m_table is set to this table, and set_base_table() is called on all Columns and LinkMaps in
682
    // the query expression tree so that they can set/update their internals as required.
683
    //
684
    // During thread-handover of a Query, set_base_table() is also called to make objects point at the new table
685
    // instead of the old one from the old thread.
686
    virtual void set_base_table(ConstTableRef) {}
59,163✔
687

688
    virtual std::string description(util::serializer::SerialisationState& state) const = 0;
689

690
    virtual void set_cluster(const Cluster*) {}
83,823✔
691

692
    // Recursively fetch tables of columns in expression tree. Used when user first builds a stand-alone expression
693
    // and
694
    // binds it to a Query at a later time
695
    virtual ConstTableRef get_base_table() const
696
    {
55,152✔
697
        return nullptr;
55,152✔
698
    }
55,152✔
699

700
    virtual void collect_dependencies(std::vector<TableKey>&) const {}
3,216✔
701

702
    virtual bool has_constant_evaluation() const
703
    {
135,450✔
704
        return false;
135,450✔
705
    }
135,450✔
706

707
    virtual bool has_single_value() const
708
    {
80,868✔
709
        return false;
80,868✔
710
    }
80,868✔
711

712
    virtual bool has_multiple_values() const
713
    {
×
714
        return false;
×
715
    }
×
716

717
    virtual bool has_search_index() const
718
    {
21,498✔
719
        return false;
21,498✔
720
    }
21,498✔
721

722
    virtual std::vector<ObjKey> find_all(Mixed) const
723
    {
×
724
        return {};
×
725
    }
×
726

727
    virtual ConstTableRef get_target_table() const
728
    {
25,254✔
729
        return {};
25,254✔
730
    }
25,254✔
731

732
    virtual DataType get_type() const = 0;
733

734
    virtual void evaluate(size_t index, ValueBase& destination) = 0;
735
    // This function supports SubColumnAggregate
736
    virtual void evaluate(ObjKey, ValueBase&)
737
    {
×
738
        REALM_ASSERT(false); // Unimplemented
×
739
    }
×
740

741
    virtual Mixed get_mixed() const
742
    {
×
743
        return {};
×
744
    }
×
745

746
    virtual util::Optional<ExpressionComparisonType> get_comparison_type() const
747
    {
978,294✔
748
        return util::none;
978,294✔
749
    }
978,294✔
750
};
751

752
template <typename T, typename... Args>
753
std::unique_ptr<Subexpr> make_subexpr(Args&&... args)
754
{
102,914✔
755
    return std::unique_ptr<Subexpr>(new T(std::forward<Args>(args)...));
102,914✔
756
}
102,914✔
757

758
template <class T>
759
class Columns;
760
template <class T>
761
class Value;
762
class ConstantMixedValue;
763
template <class T>
764
class Subexpr2;
765
template <class oper>
766
class Operator;
767
template <class oper, class TLeft = Subexpr>
768
class UnaryOperator;
769
template <class oper, class TLeft = Subexpr>
770
class SizeOperator;
771
template <class oper>
772
class TypeOfValueOperator;
773
template <class TCond>
774
class Compare;
775
template <bool has_links>
776
class UnaryLinkCompare;
777
class ColumnAccessorBase;
778

779

780
// Handle cases where left side is a constant (int, float, int64_t, double, StringData)
781
template <class Cond, class L, class R>
782
Query create(L left, const Subexpr2<R>& right)
783
{
31,994✔
784
    // Purpose of below code is to intercept the creation of a condition and test if it's supported by the old
15,997✔
785
    // query_engine.hpp which is faster. If it's supported, create a query_engine.hpp node, otherwise create a
15,997✔
786
    // query_expression.hpp node.
15,997✔
787
    //
15,997✔
788
    // This method intercepts only Value <cond> Subexpr2. Interception of Subexpr2 <cond> Subexpr is elsewhere.
15,997✔
789

15,997✔
790
    constexpr const bool supported_by_old_query_engine =
31,994✔
791
        (std::numeric_limits<L>::is_integer && std::numeric_limits<R>::is_integer) || std::is_same_v<R, Mixed> ||
31,994✔
792
        (std::is_same_v<L, R> &&
15,997✔
793
         realm::is_any_v<L, double, float, Timestamp, StringData, BinaryData, ObjectId, UUID>);
×
794

15,997✔
795
    if constexpr (REALM_OLDQUERY_FALLBACK && supported_by_old_query_engine) {
31,994✔
796
        const Columns<R>* column = dynamic_cast<const Columns<R>*>(&right);
28,594✔
797
        // TODO: recognize size operator expressions
14,297✔
798
        // auto size_operator = dynamic_cast<const SizeOperator<Size<StringData>, Subexpr>*>(&right);
14,297✔
799

14,297✔
800
        if (column && !column->links_exist()) {
28,594!
801
            ConstTableRef t = column->get_base_table();
21,424✔
802
            Query q(t);
21,424✔
803

10,712✔
804
            if constexpr (std::is_same_v<Cond, Less>)
21,424✔
805
                q.greater(column->column_key(), static_cast<R>(left));
523✔
806
            else if constexpr (std::is_same_v<Cond, Greater>)
20,378✔
807
                q.less(column->column_key(), static_cast<R>(left));
485✔
808
            else if constexpr (std::is_same_v<Cond, Equal>)
19,408✔
809
                q.equal(column->column_key(), static_cast<R>(left));
3,515✔
810
            else if constexpr (std::is_same_v<Cond, NotEqual>)
12,378✔
811
                q.not_equal(column->column_key(), static_cast<R>(left));
1,674✔
812
            else if constexpr (std::is_same_v<Cond, LessEqual>)
9,030✔
813
                q.greater_equal(column->column_key(), static_cast<R>(left));
476✔
814
            else if constexpr (std::is_same_v<Cond, GreaterEqual>)
8,078✔
815
                q.less_equal(column->column_key(), static_cast<R>(left));
478✔
816
            else if constexpr (std::is_same_v<Cond, EqualIns>)
7,122✔
817
                q.equal(column->column_key(), left, false);
357✔
818
            else if constexpr (std::is_same_v<Cond, NotEqualIns>)
6,408✔
819
                q.not_equal(column->column_key(), left, false);
351✔
820
            else if constexpr (std::is_same_v<Cond, BeginsWith>)
5,706✔
821
                q.begins_with(column->column_key(), left);
354✔
822
            else if constexpr (std::is_same_v<Cond, BeginsWithIns>)
4,998✔
823
                q.begins_with(column->column_key(), left, false);
354✔
824
            else if constexpr (std::is_same_v<Cond, EndsWith>)
4,290✔
825
                q.ends_with(column->column_key(), left);
354✔
826
            else if constexpr (std::is_same_v<Cond, EndsWithIns>)
3,582✔
827
                q.ends_with(column->column_key(), left, false);
354✔
828
            else if constexpr (std::is_same_v<Cond, Contains>)
2,874✔
829
                q.contains(column->column_key(), left);
363✔
830
            else if constexpr (std::is_same_v<Cond, ContainsIns>)
2,148✔
831
                q.contains(column->column_key(), left, false);
360✔
832
            else if constexpr (std::is_same_v<Cond, Like>)
1,428✔
833
                q.like(column->column_key(), left);
351✔
834
            else if constexpr (std::is_same_v<Cond, LikeIns>)
726✔
835
                q.like(column->column_key(), left, false);
726✔
836
            else {
21,424✔
837
                // query_engine.hpp does not support this Cond. Please either add support for it in query_engine.hpp
10,712✔
838
                // or fallback to using use 'return new Compare<>' instead.
10,712✔
839
                REALM_ASSERT(false);
21,424✔
840
            }
21,424✔
841
            return q;
21,424✔
842
        }
21,424✔
843
    }
7,170✔
844

3,585✔
845
    // Return query_expression.hpp node
3,585✔
846
    if constexpr (std::is_same_v<L, TypeOfValue>) {
10,570✔
847
        return make_expression<Compare<Cond>>(make_subexpr<Value<TypeOfValue>>(left), right.clone());
10,548✔
848
    }
10,548✔
849
    else {
10,548✔
850
        return make_expression<Compare<Cond>>(make_subexpr<ConstantMixedValue>(left), right.clone());
10,548✔
851
    }
10,548✔
852
}
7,170✔
853

854
// Purpose of this method is to intercept the creation of a condition and test if it's supported by the old
855
// query_engine.hpp which is faster. If it's supported, create a query_engine.hpp node, otherwise create a
856
// query_expression.hpp node.
857
//
858
// This method intercepts Subexpr2 <cond> Subexpr2 only. Value <cond> Subexpr2 is intercepted elsewhere.
859
template <class Cond, typename L, typename R>
860
Query create2(const Subexpr2<L>& left, const Subexpr2<R>& right)
861
{
3,800✔
862
#ifdef REALM_OLDQUERY_FALLBACK // if not defined, never fallback query_engine; always use query_expression
3,800✔
863
    // Test if expressions are of type Columns. Other possibilities are Value and Operator.
1,900✔
864
    const Columns<L>* left_col = dynamic_cast<const Columns<L>*>(&left);
3,800✔
865
    const Columns<R>* right_col = dynamic_cast<const Columns<R>*>(&right);
3,800✔
866

1,900✔
867
    // query_engine supports 'T-column <op> <T-column>' for T = {int64_t, float, double}, op = {<, >, ==, !=, <=,
1,900✔
868
    // >=},
1,900✔
869
    // but only if both columns are non-nullable, and aren't in linked tables.
1,900✔
870
    if (left_col && right_col) {
3,800✔
871
        ConstTableRef t = left_col->get_base_table();
3,688✔
872
        ConstTableRef t_right = right_col->get_base_table();
3,688✔
873
        REALM_ASSERT_DEBUG(t);
3,688✔
874
        REALM_ASSERT_DEBUG(t_right);
3,688✔
875
        // we only support multi column comparisons if they stem from the same table
1,844✔
876
        if (t->get_key() != t_right->get_key()) {
3,688✔
877
            throw Exception(
2✔
878
                ErrorCodes::InvalidQuery,
2✔
879
                util::format(
2✔
880
                    "Comparison between two properties must be linked with a relationship or exist on the same "
2✔
881
                    "Table (%1 and %2)",
2✔
882
                    t->get_name(), t_right->get_name()));
2✔
883
        }
2✔
884
        if (!left_col->links_exist() && !right_col->links_exist()) {
3,686!
885
            if constexpr (std::is_same_v<Cond, Less>)
3,674✔
886
                return Query(t).less(left_col->column_key(), right_col->column_key());
195✔
887
            if constexpr (std::is_same_v<Cond, Greater>)
3,284✔
888
                return Query(t).greater(left_col->column_key(), right_col->column_key());
205✔
889
            if constexpr (std::is_same_v<Cond, Equal>)
2,874✔
890
                return Query(t).equal(left_col->column_key(), right_col->column_key());
679✔
891
            if constexpr (std::is_same_v<Cond, NotEqual>)
1,708✔
892
                return Query(t).not_equal(left_col->column_key(), right_col->column_key());
471✔
893
            if constexpr (std::is_same_v<Cond, LessEqual>)
766✔
894
                return Query(t).less_equal(left_col->column_key(), right_col->column_key());
192✔
895
            if constexpr (std::is_same_v<Cond, GreaterEqual>)
382✔
896
                return Query(t).greater_equal(left_col->column_key(), right_col->column_key());
191✔
897
        }
×
898
    }
3,686✔
899
#endif
228✔
900
    // Return query_expression.hpp node
114✔
901
    return make_expression<Compare<Cond>>(left.clone(), right.clone());
220✔
902
}
228✔
903

904
// All overloads where left-hand-side is Subexpr2<L>:
905
//
906
// left-hand-side       operator                              right-hand-side
907
// Subexpr2<L>          +, -, *, /, <, >, ==, !=, <=, >=      R, Subexpr2<R>
908
//
909
// For L = R = {int, int64_t, float, double, StringData, Timestamp}:
910
template <class L, class R>
911
class Overloads {
912
public:
913
    // Compare, right side constant
914
    friend Query operator>(const Subexpr2<L>& left, R right)
915
    {
5,002✔
916
        return create<Less>(right, left);
5,002✔
917
    }
5,002✔
918
    friend Query operator<(const Subexpr2<L>& left, R right)
919
    {
992✔
920
        return create<Greater>(right, left);
992✔
921
    }
992✔
922
    friend Query operator>=(const Subexpr2<L>& left, R right)
923
    {
970✔
924
        return create<LessEqual>(right, left);
970✔
925
    }
970✔
926
    friend Query operator<=(const Subexpr2<L>& left, R right)
927
    {
958✔
928
        return create<GreaterEqual>(right, left);
958✔
929
    }
958✔
930
    friend Query operator==(const Subexpr2<L>& left, R right)
931
    {
5,160✔
932
        return create<Equal>(right, left);
5,160✔
933
    }
5,160✔
934
    friend Query operator!=(const Subexpr2<L>& left, R right)
935
    {
2,806✔
936
        return create<NotEqual>(right, left);
2,806✔
937
    }
2,806✔
938

939
    // Compare left-side constant
940
    friend Query operator>(R left, const Subexpr2<L>& right)
941
    {
4✔
942
        return create<Greater>(left, right);
4✔
943
    }
4✔
944
    friend Query operator<(R left, const Subexpr2<L>& right)
945
    {
6✔
946
        return create<Less>(left, right);
6✔
947
    }
6✔
948
    friend Query operator>=(R left, const Subexpr2<L>& right)
949
    {
6✔
950
        return create<GreaterEqual>(left, right);
6✔
951
    }
6✔
952
    friend Query operator<=(R left, const Subexpr2<L>& right)
953
    {
6✔
954
        return create<LessEqual>(left, right);
6✔
955
    }
6✔
956
    friend Query operator==(R left, const Subexpr2<L>& right)
957
    {
8✔
958
        return create<Equal>(left, right);
8✔
959
    }
8✔
960
    friend Query operator!=(R left, const Subexpr2<L>& right)
961
    {
4✔
962
        return create<NotEqual>(left, right);
4✔
963
    }
4✔
964

965

966
    // Compare, right side subexpression
967
    friend Query operator==(const Subexpr2<L>& left, const Subexpr2<R>& right)
968
    {
1,260✔
969
        return create2<Equal>(left, right);
1,260✔
970
    }
1,260✔
971
    friend Query operator!=(const Subexpr2<L>& left, const Subexpr2<R>& right)
972
    {
942✔
973
        return create2<NotEqual>(left, right);
942✔
974
    }
942✔
975
    friend Query operator>(const Subexpr2<L>& left, const Subexpr2<R>& right)
976
    {
422✔
977
        return create2<Greater>(left, right);
422✔
978
    }
422✔
979
    friend Query operator<(const Subexpr2<L>& left, const Subexpr2<R>& right)
980
    {
404✔
981
        return create2<Less>(left, right);
404✔
982
    }
404✔
983
    friend Query operator>=(const Subexpr2<L>& left, const Subexpr2<R>& right)
984
    {
382✔
985
        return create2<GreaterEqual>(left, right);
382✔
986
    }
382✔
987
    friend Query operator<=(const Subexpr2<L>& left, const Subexpr2<R>& right)
988
    {
390✔
989
        return create2<LessEqual>(left, right);
390✔
990
    }
390✔
991
};
992

993
// With this wrapper class we can define just 20 overloads inside Overloads<L, R> instead of 5 * 20 = 100. Todo: We
994
// can
995
// consider if it's simpler/better to remove this class completely and just list all 100 overloads manually anyway.
996
template <class T>
997
class Subexpr2 : public Subexpr,
998
                 public Overloads<T, int>,
999
                 public Overloads<T, float>,
1000
                 public Overloads<T, double>,
1001
                 public Overloads<T, int64_t>,
1002
                 public Overloads<T, StringData>,
1003
                 public Overloads<T, bool>,
1004
                 public Overloads<T, Timestamp>,
1005
                 public Overloads<T, ObjectId>,
1006
                 public Overloads<T, Decimal128>,
1007
                 public Overloads<T, UUID>,
1008
                 public Overloads<T, Mixed>,
1009
                 public Overloads<T, null> {
1010
public:
1011
    DataType get_type() const override
1012
    {
1,852,837✔
1013
        return ColumnTypeTraits<T>::id;
1,852,837✔
1014
    }
1,852,837✔
1015
};
1016

1017
template <class Operator>
1018
Query compare(const Subexpr2<Link>& left, const Obj& obj);
1019
template <class Operator>
1020
Query compare(const Subexpr2<Link>& left, null obj);
1021

1022
// Subexpr2<Link> only provides equality comparisons. Their implementations can be found later in this file.
1023
template <>
1024
class Subexpr2<Link> : public Subexpr {
1025
public:
1026
    DataType get_type() const
1027
    {
×
1028
        return type_Link;
×
1029
    }
×
1030

1031
    friend Query operator==(const Subexpr2<Link>& left, const Obj& row)
1032
    {
174✔
1033
        return compare<Equal>(left, row);
174✔
1034
    }
174✔
1035
    friend Query operator!=(const Subexpr2<Link>& left, const Obj& row)
1036
    {
106✔
1037
        return compare<NotEqual>(left, row);
106✔
1038
    }
106✔
1039
    friend Query operator==(const Obj& row, const Subexpr2<Link>& right)
1040
    {
×
1041
        return compare<Equal>(right, row);
×
1042
    }
×
1043
    friend Query operator!=(const Obj& row, const Subexpr2<Link>& right)
1044
    {
×
1045
        return compare<NotEqual>(right, row);
×
1046
    }
×
1047

1048
    friend Query operator==(const Subexpr2<Link>& left, null)
1049
    {
6✔
1050
        return compare<Equal>(left, null());
6✔
1051
    }
6✔
1052
    friend Query operator!=(const Subexpr2<Link>& left, null)
1053
    {
36✔
1054
        return compare<NotEqual>(left, null());
36✔
1055
    }
36✔
1056
    friend Query operator==(null, const Subexpr2<Link>& right)
1057
    {
×
1058
        return compare<Equal>(right, null());
×
1059
    }
×
1060
    friend Query operator!=(null, const Subexpr2<Link>& right)
1061
    {
×
1062
        return compare<NotEqual>(right, null());
×
1063
    }
×
1064

1065
    friend Query operator==(const Subexpr2<Link>& left, const Subexpr2<Link>& right)
1066
    {
2✔
1067
        return make_expression<Compare<Equal>>(left.clone(), right.clone());
2✔
1068
    }
2✔
1069
    friend Query operator!=(const Subexpr2<Link>& left, const Subexpr2<Link>& right)
1070
    {
2✔
1071
        return make_expression<Compare<NotEqual>>(left.clone(), right.clone());
2✔
1072
    }
2✔
1073
};
1074

1075
template <>
1076
class Subexpr2<StringData> : public Subexpr, public Overloads<StringData, StringData> {
1077
public:
1078
    Query equal(StringData sd, bool case_sensitive = true);
1079
    Query equal(const Subexpr2<StringData>& col, bool case_sensitive = true);
1080
    Query not_equal(StringData sd, bool case_sensitive = true);
1081
    Query not_equal(const Subexpr2<StringData>& col, bool case_sensitive = true);
1082
    Query begins_with(StringData sd, bool case_sensitive = true);
1083
    Query begins_with(const Subexpr2<StringData>& col, bool case_sensitive = true);
1084
    Query ends_with(StringData sd, bool case_sensitive = true);
1085
    Query ends_with(const Subexpr2<StringData>& col, bool case_sensitive = true);
1086
    Query contains(StringData sd, bool case_sensitive = true);
1087
    Query contains(const Subexpr2<StringData>& col, bool case_sensitive = true);
1088
    Query like(StringData sd, bool case_sensitive = true);
1089
    Query like(const Subexpr2<StringData>& col, bool case_sensitive = true);
1090
    DataType get_type() const final
1091
    {
31,068✔
1092
        return type_String;
31,068✔
1093
    }
31,068✔
1094
};
1095

1096
template <>
1097
class Subexpr2<BinaryData> : public Subexpr, public Overloads<BinaryData, BinaryData> {
1098
public:
1099
    Query equal(BinaryData sd, bool case_sensitive = true);
1100
    Query equal(const Subexpr2<BinaryData>& col, bool case_sensitive = true);
1101
    Query not_equal(BinaryData sd, bool case_sensitive = true);
1102
    Query not_equal(const Subexpr2<BinaryData>& col, bool case_sensitive = true);
1103
    Query begins_with(BinaryData sd, bool case_sensitive = true);
1104
    Query begins_with(const Subexpr2<BinaryData>& col, bool case_sensitive = true);
1105
    Query ends_with(BinaryData sd, bool case_sensitive = true);
1106
    Query ends_with(const Subexpr2<BinaryData>& col, bool case_sensitive = true);
1107
    Query contains(BinaryData sd, bool case_sensitive = true);
1108
    Query contains(const Subexpr2<BinaryData>& col, bool case_sensitive = true);
1109
    Query like(BinaryData sd, bool case_sensitive = true);
1110
    Query like(const Subexpr2<BinaryData>& col, bool case_sensitive = true);
1111
    DataType get_type() const final
1112
    {
6,464✔
1113
        return type_Binary;
6,464✔
1114
    }
6,464✔
1115
};
1116

1117
template <>
1118
class Subexpr2<Mixed> : public Subexpr,
1119
                        public Overloads<Mixed, Mixed>,
1120
                        public Overloads<Mixed, int>,
1121
                        public Overloads<Mixed, float>,
1122
                        public Overloads<Mixed, double>,
1123
                        public Overloads<Mixed, int64_t>,
1124
                        public Overloads<Mixed, StringData>,
1125
                        public Overloads<Mixed, bool>,
1126
                        public Overloads<Mixed, Timestamp>,
1127
                        public Overloads<Mixed, ObjectId>,
1128
                        public Overloads<Mixed, Decimal128>,
1129
                        public Overloads<Mixed, UUID>,
1130
                        public Overloads<Mixed, null> {
1131
public:
1132
    Query equal(Mixed sd, bool case_sensitive = true);
1133
    Query equal(const Subexpr2<Mixed>& col, bool case_sensitive = true);
1134
    Query not_equal(Mixed sd, bool case_sensitive = true);
1135
    Query not_equal(const Subexpr2<Mixed>& col, bool case_sensitive = true);
1136
    Query begins_with(Mixed sd, bool case_sensitive = true);
1137
    Query begins_with(const Subexpr2<Mixed>& col, bool case_sensitive = true);
1138
    Query ends_with(Mixed sd, bool case_sensitive = true);
1139
    Query ends_with(const Subexpr2<Mixed>& col, bool case_sensitive = true);
1140
    Query contains(Mixed sd, bool case_sensitive = true);
1141
    Query contains(const Subexpr2<Mixed>& col, bool case_sensitive = true);
1142
    Query like(Mixed sd, bool case_sensitive = true);
1143
    Query like(const Subexpr2<Mixed>& col, bool case_sensitive = true);
1144
    DataType get_type() const override
1145
    {
4,356✔
1146
        return type_Mixed;
4,356✔
1147
    }
4,356✔
1148

1149
    using T = Mixed; // used inside the following macros for operator overloads
1150
};
1151

1152
template <>
1153
class Subexpr2<TypeOfValue> : public Subexpr, public Overloads<TypeOfValue, TypeOfValue> {
1154
public:
1155
    Query equal(TypeOfValue v);
1156
    Query equal(const TypeOfValueOperator<Mixed>& col);
1157
    Query not_equal(TypeOfValue v);
1158
    Query not_equal(const TypeOfValueOperator<Mixed>& col);
1159
    DataType get_type() const final
1160
    {
1,504✔
1161
        return type_TypeOfValue;
1,504✔
1162
    }
1,504✔
1163
};
1164

1165
#if REALM_ENABLE_GEOSPATIAL
1166
template <>
1167
class Subexpr2<Geospatial> : public Subexpr, public Overloads<Geospatial, Geospatial> {
1168
public:
1169
    DataType get_type() const final
1170
    {
×
1171
        return type_Geospatial;
×
1172
    }
×
1173
};
1174
#endif
1175

1176
struct TrueExpression : Expression {
1177
    size_t find_first(size_t start, size_t end) const override
1178
    {
1,096✔
1179
        REALM_ASSERT(start <= end);
1,096✔
1180
        if (start != end)
1,096✔
1181
            return start;
1,092✔
1182

2✔
1183
        return realm::not_found;
4✔
1184
    }
4✔
1185
    void set_base_table(ConstTableRef) override {}
460✔
1186
    void set_cluster(const Cluster*) override {}
680✔
1187
    ConstTableRef get_base_table() const override
1188
    {
460✔
1189
        return nullptr;
460✔
1190
    }
460✔
1191
    std::string description(util::serializer::SerialisationState&) const override
1192
    {
220✔
1193
        return "TRUEPREDICATE";
220✔
1194
    }
220✔
1195
    std::unique_ptr<Expression> clone() const override
1196
    {
736✔
1197
        return std::unique_ptr<Expression>(new TrueExpression(*this));
736✔
1198
    }
736✔
1199
};
1200

1201

1202
struct FalseExpression : Expression {
1203
    size_t find_first(size_t, size_t) const override
1204
    {
864✔
1205
        return realm::not_found;
864✔
1206
    }
864✔
1207
    void set_base_table(ConstTableRef) override {}
258✔
1208
    void set_cluster(const Cluster*) override {}
792✔
1209
    std::string description(util::serializer::SerialisationState&) const override
1210
    {
132✔
1211
        return "FALSEPREDICATE";
132✔
1212
    }
132✔
1213
    ConstTableRef get_base_table() const override
1214
    {
228✔
1215
        return nullptr;
228✔
1216
    }
228✔
1217
    std::unique_ptr<Expression> clone() const override
1218
    {
582✔
1219
        return std::unique_ptr<Expression>(new FalseExpression(*this));
582✔
1220
    }
582✔
1221
};
1222

1223

1224
// Stores N values of type T. Can also exchange data with other ValueBase of different types
1225
template <class T>
1226
class Value : public ValueBase, public Subexpr2<T> {
1227
public:
1228
    Value() = default;
6,177,005✔
1229

1230
    Value(T init)
1231
        : ValueBase(QueryValue(init))
1232
    {
525,108✔
1233
    }
525,108✔
1234

1235
    std::string value_to_string(size_t ndx, util::serializer::SerialisationState& state) const
1236
    {
13,900✔
1237
        auto val = get(ndx);
13,900✔
1238
        if (val.is_null())
13,900✔
1239
            return "NULL";
990✔
1240
        else {
12,910✔
1241
            static_cast<void>(state);
12,910✔
1242
            if constexpr (std::is_same_v<T, TypeOfValue>) {
12,910✔
1243
                return util::serializer::print_value(val.get_type_of_value());
12,658✔
1244
            }
12,658✔
1245
            else if constexpr (std::is_same_v<T, ObjKey>) {
12,658✔
1246
                ObjKey obj_key = val.template get<ObjKey>();
12,618✔
1247
                if (state.target_table) {
7,816✔
1248
                    ObjLink link(state.target_table->get_key(), obj_key);
3,014✔
1249
                    return util::serializer::print_value(link, state.group);
3,014✔
1250
                }
3,014✔
1251
                else {
2,974✔
1252
                    return util::serializer::print_value(obj_key);
2,974✔
1253
                }
2,974✔
1254
            }
3,014✔
1255
            else if constexpr (std::is_same_v<T, ObjLink>) {
12,618✔
1256
                return util::serializer::print_value(val.template get<ObjLink>(), state.group);
12,614✔
1257
            }
12,614✔
1258
            else if constexpr (std::is_same_v<T, Mixed>) {
12,614✔
1259
                if (val.is_type(type_TypedLink)) {
6,307✔
1260
                    return util::serializer::print_value(val.template get<ObjLink>(), state.group);
6✔
1261
                }
6✔
1262
                else {
2,046✔
1263
                    return util::serializer::print_value(val);
2,046✔
1264
                }
2,046✔
1265
            }
2,052✔
1266
            else {
10,562✔
1267
                return util::serializer::print_value(val.template get<T>());
10,562✔
1268
            }
10,562✔
1269
        }
12,910✔
1270
    }
13,900✔
1271

1272
    std::string description(util::serializer::SerialisationState& state) const override
1273
    {
12,756✔
1274
        const size_t sz = size();
12,756✔
1275
        if (m_from_list) {
12,756✔
1276
            std::string desc = state.describe_expression_type(m_comparison_type) + "{";
860✔
1277
            for (size_t i = 0; i < sz; ++i) {
2,864!
1278
                if (i != 0) {
2,004!
1279
                    desc += ", ";
1,252✔
1280
                }
1,252✔
1281
                desc += value_to_string(i, state);
2,004✔
1282
            }
2,004✔
1283
            desc += "}";
860✔
1284
            return desc;
860✔
1285
        }
860✔
1286
        else if (sz == 1) {
11,896✔
1287
            return value_to_string(0, state);
11,896✔
1288
        }
11,896✔
1289
        return "";
×
1290
    }
×
1291

1292
    bool has_multiple_values() const override
1293
    {
666✔
1294
        return m_from_list;
666✔
1295
    }
666✔
1296

1297
    bool has_single_value() const override
1298
    {
547,950✔
1299
        return size() == 1;
547,950✔
1300
    }
547,950✔
1301

1302
    bool has_constant_evaluation() const override
1303
    {
93,820✔
1304
        return true;
93,820✔
1305
    }
93,820✔
1306

1307
    util::Optional<ExpressionComparisonType> get_comparison_type() const final
1308
    {
1,422,694✔
1309
        REALM_ASSERT_DEBUG(!m_comparison_type || m_from_list);
1,422,694!
1310
        return m_comparison_type;
1,422,694✔
1311
    }
1,422,694✔
1312

1313
    void set_comparison_type(util::Optional<ExpressionComparisonType> type)
1314
    {
1,596✔
1315
        m_comparison_type = type;
1,596✔
1316
    }
1,596✔
1317

1318
    Mixed get_mixed() const override
1319
    {
502,087✔
1320
        return get(0);
502,087✔
1321
    }
502,087✔
1322

1323
    void evaluate(size_t, ValueBase& destination) override
1324
    {
120✔
1325
        destination = *this;
120✔
1326
    }
120✔
1327

1328
    std::unique_ptr<Subexpr> clone() const override
1329
    {
22,858✔
1330
        return make_subexpr<Value<T>>(*this);
22,858✔
1331
    }
22,858✔
1332

1333
protected:
1334
    util::Optional<ExpressionComparisonType> m_comparison_type;
1335
};
1336

1337
class ConstantMixedValue : public Value<Mixed> {
1338
public:
1339
    ConstantMixedValue(const Mixed& val)
1340
        : Value(val)
1341
    {
22,896✔
1342
        begin()->use_buffer(m_buffer);
22,896✔
1343
    }
22,896✔
1344

1345
    std::unique_ptr<Subexpr> clone() const override
1346
    {
15,786✔
1347
        return std::unique_ptr<Subexpr>(new ConstantMixedValue(*this));
15,786✔
1348
    }
15,786✔
1349

1350
private:
1351
    ConstantMixedValue(const ConstantMixedValue& other)
1352
        : Value(other)
1353
    {
15,786✔
1354
        begin()->use_buffer(m_buffer);
15,786✔
1355
    }
15,786✔
1356

1357
    std::string m_buffer;
1358
};
1359

1360
class ConstantMixedList : public Value<Mixed> {
1361
public:
1362
    ConstantMixedList(size_t nb_values)
1363
        : Value()
1364
        , m_buffer(nb_values)
1365
    {
1,328✔
1366
        this->init(true, nb_values);
1,328✔
1367
    }
1,328✔
1368
    void set(size_t n, Mixed val)
1369
    {
3,472✔
1370
        Value<Mixed>::set(n, val);
3,472✔
1371
        (*this)[n].use_buffer(m_buffer[n]);
3,472✔
1372
    }
3,472✔
1373

1374
    std::unique_ptr<Subexpr> clone() const override
1375
    {
1,472✔
1376
        return std::unique_ptr<Subexpr>(new ConstantMixedList(*this));
1,472✔
1377
    }
1,472✔
1378

1379
private:
1380
    ConstantMixedList(const ConstantMixedList& other)
1381
        : Value(other)
1382
        , m_buffer(other.size())
1383
    {
1,472✔
1384
        for (size_t i = 0; i < size(); i++) {
5,260✔
1385
            (*this)[i].use_buffer(m_buffer[i]);
3,788✔
1386
        }
3,788✔
1387
    }
1,472✔
1388

1389
    std::vector<std::string> m_buffer;
1390
};
1391

1392
class ConstantStringValue : public Value<StringData> {
1393
public:
1394
    ConstantStringValue(const StringData& string)
1395
        : Value()
1396
        , m_string(string.is_null() ? util::none : util::make_optional(std::string(string)))
1397
    {
7,508✔
1398
        if (m_string)
7,508✔
1399
            set(0, *m_string);
7,188✔
1400
    }
7,508✔
1401

1402
    std::unique_ptr<Subexpr> clone() const override
1403
    {
4,020✔
1404
        return std::unique_ptr<Subexpr>(new ConstantStringValue(*this));
4,020✔
1405
    }
4,020✔
1406

1407
private:
1408
    ConstantStringValue(const ConstantStringValue& other)
1409
        : Value()
1410
        , m_string(other.m_string)
1411
    {
4,020✔
1412
        if (m_string)
4,020✔
1413
            set(0, *m_string);
3,892✔
1414
    }
4,020✔
1415

1416
    util::Optional<std::string> m_string;
1417
};
1418

1419
class ConstantBinaryValue : public Value<BinaryData> {
1420
public:
1421
    ConstantBinaryValue(const BinaryData& bin)
1422
        : Value()
1423
        , m_buffer(bin)
1424
    {
888✔
1425
        if (m_buffer.data())
888✔
1426
            set(0, BinaryData(m_buffer.data(), m_buffer.size()));
888✔
1427
    }
888✔
1428

1429
    std::unique_ptr<Subexpr> clone() const override
1430
    {
140✔
1431
        return std::unique_ptr<Subexpr>(new ConstantBinaryValue(*this));
140✔
1432
    }
140✔
1433

1434
private:
1435
    ConstantBinaryValue(const ConstantBinaryValue& other)
1436
        : Value()
1437
        , m_buffer(other.m_buffer)
1438
    {
140✔
1439
        if (m_buffer.data())
140✔
1440
            set(0, BinaryData(m_buffer.data(), m_buffer.size()));
140✔
1441
    }
140✔
1442

1443
    OwnedBinaryData m_buffer;
1444
};
1445

1446
#if REALM_ENABLE_GEOSPATIAL
1447
class ConstantGeospatialValue : public Value<Geospatial> {
1448
public:
1449
    ConstantGeospatialValue(const Geospatial& geo)
1450
        : Value()
1451
        , m_geospatial(geo)
1452
    {
124✔
1453
        if (geo.get_type() != Geospatial::Type::Invalid) {
124✔
1454
            set(0, Mixed{&m_geospatial});
124✔
1455
        }
124✔
1456
    }
124✔
1457

1458
    std::unique_ptr<Subexpr> clone() const override
1459
    {
×
1460
        return std::unique_ptr<Subexpr>(new ConstantGeospatialValue(*this));
×
1461
    }
×
1462

1463
private:
1464
    ConstantGeospatialValue(const ConstantGeospatialValue& other)
1465
        : Value()
1466
        , m_geospatial(other.m_geospatial)
1467
    {
×
1468
        if (m_geospatial.get_type() != Geospatial::Type::Invalid) {
×
1469
            set(0, Mixed{&m_geospatial});
×
1470
        }
×
1471
    }
×
1472
    Geospatial m_geospatial;
1473
};
1474
#endif
1475

1476
// Classes used for LinkMap (see below).
1477

1478
// Your function is given key within the linked-to table as argument, and you must return whether or not you want the
1479
// LinkMapFunction to exit (return false) or continue (return true) harvesting the link tree for the current main
1480
// table object (it will be a link tree if you have multiple type_LinkList columns in a link()->link() query.
1481

1482
using LinkMapFunction = util::FunctionRef<bool(ObjKey)>;
1483

1484
/*
1485
The LinkMap and LinkMapFunction classes are used for query conditions on links themselves (contrary to conditions on
1486
the value payload they point at).
1487

1488
MapLink::map_links() takes a row index of the link array as argument and follows any link chain stated in the query
1489
(through the link()->link() methods) until the final payload table is reached, and then applies LinkMapFunction on
1490
the linked-to key(s).
1491

1492
If all link columns are type_Link, then LinkMapFunction is only invoked for a single key. If one or more
1493
columns are type_LinkList, then it may result in multiple keys.
1494

1495
The reason we use this map pattern is that we can exit the link-tree-traversal as early as possible, e.g. when we've
1496
found the first link that points to key '5'. Other solutions could be a std::vector<ColKey> harvest_all_links(), or an
1497
iterator pattern. First solution can't exit, second solution requires internal state.
1498
*/
1499
class LinkMap final {
1500
public:
1501
    LinkMap() = default;
312✔
1502
    LinkMap(ConstTableRef table, std::vector<ColKey> columns)
1503
        : m_link_column_keys(std::move(columns))
1504
    {
534,024✔
1505
        set_base_table(table);
534,024✔
1506
    }
534,024✔
1507

1508
    LinkMap(LinkMap const& other)
1509
    {
168,465✔
1510
        m_link_column_keys = other.m_link_column_keys;
168,465✔
1511
        m_tables = other.m_tables;
168,465✔
1512
        m_link_types = other.m_link_types;
168,465✔
1513
        m_only_unary_links = other.m_only_unary_links;
168,465✔
1514
    }
168,465✔
1515

1516
    size_t get_nb_hops() const
1517
    {
2,008✔
1518
        return m_link_column_keys.size();
2,008✔
1519
    }
2,008✔
1520

1521
    bool has_links() const
1522
    {
6,017,670✔
1523
        return m_link_column_keys.size() > 0;
6,017,670✔
1524
    }
6,017,670✔
1525

1526
    ColKey get_first_column_key() const
1527
    {
280✔
1528
        REALM_ASSERT(has_links());
280✔
1529
        return m_link_column_keys[0];
280✔
1530
    }
280✔
1531

1532
    void set_base_table(ConstTableRef table);
1533

1534
    void set_cluster(const Cluster* cluster)
1535
    {
28,908✔
1536
        Allocator& alloc = get_base_table()->get_alloc();
28,908✔
1537
        ArrayPayload* array_ptr;
28,908✔
1538
        switch (m_link_types[0]) {
28,908✔
1539
            case col_type_Link:
14,268✔
1540
                if (m_link_column_keys[0].is_dictionary()) {
14,268✔
1541
                    array_ptr = &m_leaf.emplace<ArrayInteger>(alloc);
246✔
1542
                }
246✔
1543
                else {
14,022✔
1544
                    array_ptr = &m_leaf.emplace<ArrayKey>(alloc);
14,022✔
1545
                }
14,022✔
1546
                break;
14,268✔
1547
            case col_type_LinkList:
13,746✔
1548
                array_ptr = &m_leaf.emplace<ArrayList>(alloc);
13,746✔
1549
                break;
13,746✔
1550
            case col_type_BackLink:
894✔
1551
                array_ptr = &m_leaf.emplace<ArrayBacklink>(alloc);
894✔
1552
                break;
894✔
1553
            default:
✔
1554
                REALM_UNREACHABLE();
1555
        }
28,908✔
1556
        cluster->init_leaf(m_link_column_keys[0], array_ptr);
28,908✔
1557
    }
28,908✔
1558

1559
    void collect_dependencies(std::vector<TableKey>& tables) const;
1560

1561
    std::string description(util::serializer::SerialisationState& state) const;
1562

1563
    ObjKey get_unary_link_or_not_found(size_t index) const
1564
    {
55,140✔
1565
        REALM_ASSERT(m_only_unary_links);
55,140✔
1566
        ObjKey result;
55,140✔
1567
        map_links(index, [&](ObjKey key) {
54,456✔
1568
            result = key;
53,772✔
1569
            return false; // exit search, only one result ever expected
53,772✔
1570
        });
53,772✔
1571
        return result;
55,140✔
1572
    }
55,140✔
1573

1574
    std::vector<ObjKey> get_links(size_t index) const
1575
    {
237,264✔
1576
        std::vector<ObjKey> res;
237,264✔
1577
        get_links(index, res);
237,264✔
1578
        return res;
237,264✔
1579
    }
237,264✔
1580

1581
    std::vector<ObjKey> get_origin_ndxs(ObjKey key, size_t column = 0) const;
1582

1583
    size_t count_links(size_t row) const
1584
    {
22,638✔
1585
        size_t count = 0;
22,638✔
1586
        map_links(row, [&](ObjKey) {
19,749✔
1587
            ++count;
16,860✔
1588
            return true;
16,860✔
1589
        });
16,860✔
1590
        return count;
22,638✔
1591
    }
22,638✔
1592

1593
    size_t count_all_backlinks(size_t row) const
1594
    {
184✔
1595
        size_t count = 0;
184✔
1596
        auto table = get_target_table().unchecked_ptr();
184✔
1597
        map_links(row, [&](ObjKey key) {
432✔
1598
            count += table->get_object(key).get_backlink_count();
432✔
1599
            return true;
432✔
1600
        });
432✔
1601
        return count;
184✔
1602
    }
184✔
1603

1604
    void map_links(size_t row, LinkMapFunction lm) const
1605
    {
355,230✔
1606
        map_links(0, row, lm);
355,230✔
1607
    }
355,230✔
1608

1609
    bool only_unary_links() const
1610
    {
300,300✔
1611
        return m_only_unary_links;
300,300✔
1612
    }
300,300✔
1613

1614
    ConstTableRef get_base_table() const
1615
    {
1,316,613✔
1616
        return m_tables.empty() ? nullptr : m_tables[0];
1,059,654✔
1617
    }
1,316,613✔
1618

1619
    ConstTableRef get_target_table() const
1620
    {
2,492,193✔
1621
        REALM_ASSERT(!m_tables.empty());
2,492,193✔
1622
        return m_tables.back();
2,492,193✔
1623
    }
2,492,193✔
1624

1625
    bool links_exist() const
1626
    {
19,854✔
1627
        return !m_link_column_keys.empty();
19,854✔
1628
    }
19,854✔
1629

1630
    ColKey pop_last()
1631
    {
44✔
1632
        ColKey col = m_link_column_keys.back();
44✔
1633
        m_link_column_keys.pop_back();
44✔
1634
        m_tables.pop_back();
44✔
1635
        return col;
44✔
1636
    }
44✔
1637

1638
private:
1639
    bool map_links(size_t column, ObjKey key, LinkMapFunction lm) const;
1640
    void map_links(size_t column, size_t row, LinkMapFunction lm) const;
1641

1642
    void get_links(size_t row, std::vector<ObjKey>& result) const
1643
    {
237,267✔
1644
        map_links(row, [&](ObjKey key) {
344,163✔
1645
            result.push_back(key);
344,163✔
1646
            return true; // continue evaluation
344,163✔
1647
        });
344,163✔
1648
    }
237,267✔
1649

1650
    mutable std::vector<ColKey> m_link_column_keys;
1651
    std::vector<ColumnType> m_link_types;
1652
    std::vector<ConstTableRef> m_tables;
1653
    bool m_only_unary_links = true;
1654

1655
    mpark::variant<mpark::monostate, ArrayKey, ArrayInteger, ArrayList, ArrayBacklink> m_leaf;
1656

1657
    template <class>
1658
    friend Query compare(const Subexpr2<Link>&, const Obj&);
1659
};
1660

1661
template <class T>
1662
Value<T> make_value_for_link(bool only_unary_links, size_t size)
1663
{
1664
    Value<T> value;
1665
    if (only_unary_links) {
1666
        REALM_ASSERT(size <= 1);
1667
        value.init(false, 1);
1668
        value.m_storage.set_null(0);
1669
    }
1670
    else {
1671
        value.init(true, size);
1672
    }
1673
    return value;
1674
}
1675

1676
// This class can be used as untyped base for expressions that handle object properties
1677
class ObjPropertyBase {
1678
public:
1679
    ObjPropertyBase(ColKey column, ConstTableRef table, std::vector<ColKey> links,
1680
                    util::Optional<ExpressionComparisonType> type)
1681
        : m_link_map(table, std::move(links))
1682
        , m_column_key(column)
1683
        , m_comparison_type(type)
1684
    {
506,932✔
1685
    }
506,932✔
1686
    ObjPropertyBase(const ObjPropertyBase& other)
1687
        : m_link_map(other.m_link_map)
1688
        , m_column_key(other.m_column_key)
1689
        , m_comparison_type(other.m_comparison_type)
1690
    {
46,948✔
1691
    }
46,948✔
1692
    ObjPropertyBase(ColKey column, const LinkMap& link_map, util::Optional<ExpressionComparisonType> type)
1693
        : m_link_map(link_map)
1694
        , m_column_key(column)
1695
        , m_comparison_type(type)
1696
    {
×
1697
    }
×
1698

1699
    bool links_exist() const
1700
    {
3,924,999✔
1701
        return m_link_map.has_links();
3,924,999✔
1702
    }
3,924,999✔
1703

1704
    bool only_unary_links() const
1705
    {
×
1706
        return m_link_map.only_unary_links();
×
1707
    }
×
1708

1709
    bool is_nullable() const
1710
    {
1,584,508✔
1711
        return m_column_key.get_attrs().test(col_attr_Nullable);
1,584,508✔
1712
    }
1,584,508✔
1713

1714
    const LinkMap& get_link_map() const
1715
    {
12✔
1716
        return m_link_map;
12✔
1717
    }
12✔
1718

1719
    ColKey column_key() const noexcept
1720
    {
722,919✔
1721
        return m_column_key;
722,919✔
1722
    }
722,919✔
1723

1724
protected:
1725
    LinkMap m_link_map;
1726
    // Column index of payload column of m_table
1727
    mutable ColKey m_column_key;
1728
    util::Optional<ExpressionComparisonType> m_comparison_type; // Any, All, None
1729
};
1730

1731
// Combines Subexpr2<T> and ObjPropertyBase
1732
// Implements virtual functions defined in Expression/Subexpr
1733
template <class T>
1734
class ObjPropertyExpr : public Subexpr2<T>, public ObjPropertyBase {
1735
public:
1736
    using ObjPropertyBase::ObjPropertyBase;
1737

1738
    bool has_multiple_values() const override
1739
    {
184✔
1740
        return m_link_map.has_links() && !m_link_map.only_unary_links();
184!
1741
    }
184✔
1742

1743
    ConstTableRef get_base_table() const final
1744
    {
127,732✔
1745
        return m_link_map.get_base_table();
127,732✔
1746
    }
127,732✔
1747

1748
    void set_base_table(ConstTableRef table) final
1749
    {
28,256✔
1750
        if (table != get_base_table()) {
28,256✔
1751
            m_link_map.set_base_table(table);
3,152✔
1752
        }
3,152✔
1753
    }
28,256✔
1754

1755
    bool has_search_index() const final
1756
    {
7,880✔
1757
        auto target_table = m_link_map.get_target_table();
7,880✔
1758
        return target_table->search_index_type(m_column_key) == IndexType::General;
7,880✔
1759
    }
7,880✔
1760

1761
    std::vector<ObjKey> find_all(Mixed value) const final
1762
    {
680✔
1763
        std::vector<ObjKey> ret;
680✔
1764
        std::vector<ObjKey> result;
680✔
1765

340✔
1766
        if (value.is_null() && !m_column_key.is_nullable()) {
680!
1767
            return ret;
20✔
1768
        }
20✔
1769

330✔
1770
        if (m_link_map.get_target_table()->get_primary_key_column() == m_column_key) {
660!
1771
            // Only one object with a given key would be possible
1772
            if (auto k = m_link_map.get_target_table()->find_primary_key(value))
×
1773
                result.push_back(k);
×
1774
        }
×
1775
        else {
660✔
1776
            StringIndex* index = m_link_map.get_target_table()->get_search_index(m_column_key);
660✔
1777
            REALM_ASSERT(index);
660!
1778
            if (value.is_null()) {
660!
1779
                index->find_all(result, realm::null{});
24✔
1780
            }
24✔
1781
            else {
636✔
1782
                T val = value.get<T>();
636✔
1783
                index->find_all(result, val);
636✔
1784
            }
636✔
1785
        }
660✔
1786

330✔
1787
        for (ObjKey k : result) {
10,074!
1788
            auto ndxs = m_link_map.get_origin_ndxs(k);
10,074✔
1789
            ret.insert(ret.end(), ndxs.begin(), ndxs.end());
10,074✔
1790
        }
10,074✔
1791

330✔
1792
        return ret;
660✔
1793
    }
660✔
1794

1795
    void collect_dependencies(std::vector<TableKey>& tables) const final
1796
    {
684✔
1797
        m_link_map.collect_dependencies(tables);
684✔
1798
    }
684✔
1799

1800
    std::string description(util::serializer::SerialisationState& state) const override
1801
    {
1,928✔
1802
        return state.describe_expression_type(m_comparison_type) + state.describe_columns(m_link_map, m_column_key);
1,928✔
1803
    }
1,928✔
1804

1805
    util::Optional<ExpressionComparisonType> get_comparison_type() const final
1806
    {
649,784✔
1807
        return m_comparison_type;
649,784✔
1808
    }
649,784✔
1809

1810
    std::unique_ptr<Subexpr> clone() const override
1811
    {
34,604✔
1812
        return make_subexpr<Columns<T>>(static_cast<const Columns<T>&>(*this));
34,604✔
1813
    }
34,604✔
1814
};
1815

1816
// If we add a new Realm type T and quickly want Query support for it, then simply inherit from it like
1817
// `template <> class Columns<T> : public SimpleQuerySupport<T>` and you're done. Any operators of the set
1818
// { ==, >=, <=, !=, >, < } that are supported by T will be supported by the "query expression syntax"
1819
// automatically. NOTE: This method of Query support will be slow because it goes through Table::get<T>.
1820
// To get faster Query support, either add SequentialGetter support (faster) or create a query_engine.hpp
1821
// node for it (super fast).
1822

1823
template <class T>
1824
class SimpleQuerySupport : public ObjPropertyExpr<T> {
1825
public:
1826
    using ObjPropertyExpr<T>::links_exist;
1827

1828
    SimpleQuerySupport(ColKey column, ConstTableRef table, std::vector<ColKey> links = {},
1829
                       util::Optional<ExpressionComparisonType> type = util::none)
1830
        : ObjPropertyExpr<T>(column, table, std::move(links), type)
1831
    {
462,812✔
1832
    }
462,812✔
1833

1834
    void set_cluster(const Cluster* cluster) override
1835
    {
19,752✔
1836
        if (links_exist()) {
19,752✔
1837
            m_link_map.set_cluster(cluster);
4,220✔
1838
        }
4,220✔
1839
        else {
15,532✔
1840
            m_leaf.emplace(m_link_map.get_base_table()->get_alloc());
15,532✔
1841
            cluster->init_leaf(m_column_key, &*m_leaf);
15,532✔
1842
        }
15,532✔
1843
    }
19,752✔
1844

1845
    void evaluate(size_t index, ValueBase& destination) override
1846
    {
549,012✔
1847
        if (links_exist()) {
549,012✔
1848
            REALM_ASSERT(!m_leaf);
39,852✔
1849

19,926✔
1850
            if (m_link_map.only_unary_links()) {
39,852✔
1851
                REALM_ASSERT(destination.size() == 1);
35,004✔
1852
                REALM_ASSERT(!destination.m_from_list);
35,004✔
1853
                destination.set_null(0);
35,004✔
1854
                auto link_translation_key = this->m_link_map.get_unary_link_or_not_found(index);
35,004✔
1855
                if (link_translation_key) {
35,004✔
1856
                    const Obj obj = m_link_map.get_target_table()->get_object(link_translation_key);
34,084✔
1857
                    if constexpr (realm::is_any_v<T, ObjectId, UUID>) {
34,084✔
1858
                        auto opt_val = obj.get<util::Optional<T>>(m_column_key);
16,036✔
1859
                        if (opt_val) {
18,048✔
1860
                            destination.set(0, *opt_val);
2,208✔
1861
                        }
2,208✔
1862
                        else {
15,840✔
1863
                            destination.set_null(0);
15,840✔
1864
                        }
15,840✔
1865
                    }
18,048✔
1866
                    else {
16,036✔
1867
                        destination.set(0, obj.get<T>(m_column_key));
16,036✔
1868
                    }
16,036✔
1869
                }
34,084✔
1870
            }
35,004✔
1871
            else {
4,848✔
1872
                std::vector<ObjKey> links = m_link_map.get_links(index);
4,848✔
1873
                destination.init(true, links.size());
4,848✔
1874
                for (size_t t = 0; t < links.size(); t++) {
39,976✔
1875
                    const Obj obj = m_link_map.get_target_table()->get_object(links[t]);
35,128✔
1876
                    if constexpr (realm::is_any_v<T, ObjectId, UUID>) {
35,128✔
1877
                        auto opt_val = obj.get<util::Optional<T>>(m_column_key);
23,128✔
1878
                        if (opt_val) {
17,564✔
1879
                            destination.set(t, *opt_val);
408✔
1880
                        }
408✔
1881
                        else {
11,592✔
1882
                            destination.set_null(t);
11,592✔
1883
                        }
11,592✔
1884
                    }
12,000✔
1885
                    else {
23,128✔
1886
                        destination.set(t, obj.get<T>(m_column_key));
23,128✔
1887
                    }
23,128✔
1888
                }
35,128✔
1889
            }
4,848✔
1890
        }
39,852✔
1891
        else {
509,160✔
1892
            // Not a link column
254,580✔
1893
            REALM_ASSERT(m_leaf);
509,160✔
1894
            REALM_ASSERT(destination.size() == 1);
509,160✔
1895
            REALM_ASSERT(!destination.m_from_list);
509,160✔
1896
            if (m_leaf->is_null(index)) {
509,160✔
1897
                destination.set_null(0);
60,524✔
1898
            }
60,524✔
1899
            else {
448,636✔
1900
                destination.set(0, m_leaf->get(index));
448,636✔
1901
            }
448,636✔
1902
        }
509,160✔
1903
    }
549,012✔
1904

1905
    void evaluate(ObjKey key, ValueBase& destination) override
1906
    {
2,320✔
1907
        Value<T>& d = static_cast<Value<T>&>(destination);
2,320✔
1908
        d.set(0, m_link_map.get_target_table()->get_object(key).template get<T>(m_column_key));
2,320✔
1909
    }
2,320✔
1910

1911
    SimpleQuerySupport(const SimpleQuerySupport& other)
1912
        : ObjPropertyExpr<T>(other)
1913
    {
21,520✔
1914
    }
21,520✔
1915

1916
    SizeOperator<T> size()
1917
    {
656✔
1918
        return SizeOperator<T>(this->clone());
656✔
1919
    }
656✔
1920

1921
    TypeOfValueOperator<T> type_of_value()
1922
    {
480✔
1923
        return TypeOfValueOperator<T>(this->clone());
480✔
1924
    }
480✔
1925

1926
private:
1927
    using ObjPropertyExpr<T>::m_link_map;
1928
    using ObjPropertyExpr<T>::m_column_key;
1929

1930
    using LeafType = typename ColumnTypeTraits<T>::cluster_leaf_type;
1931
    std::optional<LeafType> m_leaf;
1932
};
1933

1934
template <>
1935
class Columns<Timestamp> : public SimpleQuerySupport<Timestamp> {
1936
    using SimpleQuerySupport::SimpleQuerySupport;
1937
};
1938

1939
template <>
1940
class Columns<BinaryData> : public SimpleQuerySupport<BinaryData> {
1941
    using SimpleQuerySupport::SimpleQuerySupport;
1942

1943
    friend Query operator==(const Columns<BinaryData>& left, BinaryData right)
1944
    {
316✔
1945
        return create<Equal>(right, left);
316✔
1946
    }
316✔
1947

1948
    friend Query operator==(BinaryData left, const Columns<BinaryData>& right)
1949
    {
4✔
1950
        return create<Equal>(left, right);
4✔
1951
    }
4✔
1952

1953
    friend Query operator!=(const Columns<BinaryData>& left, BinaryData right)
1954
    {
238✔
1955
        return create<NotEqual>(right, left);
238✔
1956
    }
238✔
1957

1958
    friend Query operator!=(BinaryData left, const Columns<BinaryData>& right)
1959
    {
2✔
1960
        return create<NotEqual>(left, right);
2✔
1961
    }
2✔
1962

1963
    friend Query operator==(const Columns<BinaryData>& left, realm::null)
1964
    {
12✔
1965
        return create<Equal>(BinaryData(), left);
12✔
1966
    }
12✔
1967

1968
    friend Query operator==(realm::null, const Columns<BinaryData>& right)
1969
    {
×
1970
        return create<Equal>(BinaryData(), right);
×
1971
    }
×
1972

1973
    friend Query operator!=(const Columns<BinaryData>& left, realm::null)
1974
    {
×
1975
        return create<NotEqual>(BinaryData(), left);
×
1976
    }
×
1977

1978
    friend Query operator!=(realm::null, const Columns<BinaryData>& right)
1979
    {
×
1980
        return create<NotEqual>(BinaryData(), right);
×
1981
    }
×
1982
};
1983

1984
template <>
1985
class Columns<ObjectId> : public SimpleQuerySupport<ObjectId> {
1986
    using SimpleQuerySupport::SimpleQuerySupport;
1987
};
1988

1989
template <>
1990
class Columns<Decimal128> : public SimpleQuerySupport<Decimal128> {
1991
    using SimpleQuerySupport::SimpleQuerySupport;
1992
};
1993

1994
template <>
1995
class Columns<Mixed> : public SimpleQuerySupport<Mixed> {
1996
    using SimpleQuerySupport::SimpleQuerySupport;
1997
};
1998

1999
template <>
2000
class Columns<UUID> : public SimpleQuerySupport<UUID> {
2001
    using SimpleQuerySupport::SimpleQuerySupport;
2002
};
2003

2004
template <class T, class S, class I>
2005
inline std::enable_if_t<!realm::is_any_v<T, StringData, realm::null, const char*, std::string>, Query>
2006
string_compare(const Subexpr2<StringData>& left, T right, bool)
2007
{
×
2008
    return make_expression<Compare<Equal>>(right.clone(), left.clone());
×
2009
}
×
2010

2011
template <class T, class S, class I>
2012
inline std::enable_if_t<realm::is_any_v<T, StringData, realm::null, const char*, std::string>, Query>
2013
string_compare(const Subexpr2<StringData>& left, T right, bool case_sensitive)
2014
{
12,334✔
2015
    StringData sd(right);
12,334✔
2016
    if (case_sensitive)
12,334✔
2017
        return create<S>(sd, left);
7,990✔
2018
    else
4,344✔
2019
        return create<I>(sd, left);
4,344✔
2020
}
12,334✔
2021

2022
template <class S, class I>
2023
Query string_compare(const Subexpr2<StringData>& left, const Subexpr2<StringData>& right, bool case_sensitive)
2024
{
3,960✔
2025
    if (case_sensitive)
3,960✔
2026
        return make_expression<Compare<S>>(right.clone(), left.clone());
2,268✔
2027
    else
1,692✔
2028
        return make_expression<Compare<I>>(right.clone(), left.clone());
1,692✔
2029
}
3,960✔
2030

2031
template <>
2032
class Columns<StringData> : public SimpleQuerySupport<StringData> {
2033
public:
2034
    Columns(ColKey column, ConstTableRef table, std::vector<ColKey> links = {},
2035
            util::Optional<ExpressionComparisonType> type = util::none)
2036
        : SimpleQuerySupport(column, table, links, type)
2037
    {
18,760✔
2038
    }
18,760✔
2039

2040
    Columns(Columns const& other)
2041
        : SimpleQuerySupport(other)
2042
    {
12,468✔
2043
    }
12,468✔
2044

2045
    Columns(Columns&& other) noexcept
2046
        : SimpleQuerySupport(other)
2047
    {
×
2048
    }
×
2049

2050
    Query fulltext(StringData sd) const;
2051

2052
    using SimpleQuerySupport::size;
2053

2054
    // Columns<String> == Columns<String>
2055
    friend Query operator==(const Columns<StringData>& left, const Columns<StringData>& right)
2056
    {
192✔
2057
        return string_compare<Equal, EqualIns>(left, right, true);
192✔
2058
    }
192✔
2059

2060
    // Columns<String> != Columns<String>
2061
    friend Query operator!=(const Columns<StringData>& left, const Columns<StringData>& right)
2062
    {
188✔
2063
        return string_compare<NotEqual, NotEqualIns>(left, right, true);
188✔
2064
    }
188✔
2065

2066
    // String == Columns<String>
2067
    template <class T>
2068
    friend Query operator==(T left, const Columns<StringData>& right)
2069
    {
4✔
2070
        return operator==(right, left);
4✔
2071
    }
4✔
2072

2073
    // String != Columns<String>
2074
    template <class T>
2075
    friend Query operator!=(T left, const Columns<StringData>& right)
2076
    {
2✔
2077
        return operator!=(right, left);
2✔
2078
    }
2✔
2079

2080
    // Columns<String> == String
2081
    template <class T>
2082
    friend Query operator==(const Columns<StringData>& left, T right)
2083
    {
1,644✔
2084
        return string_compare<T, Equal, EqualIns>(left, right, true);
1,644✔
2085
    }
1,644✔
2086

2087
    // Columns<String> != String
2088
    template <class T>
2089
    friend Query operator!=(const Columns<StringData>& left, T right)
2090
    {
482✔
2091
        return string_compare<T, NotEqual, NotEqualIns>(left, right, true);
482✔
2092
    }
482✔
2093
};
2094

2095
template <class T, class S, class I>
2096
Query binary_compare(const Subexpr2<BinaryData>& left, T right, bool case_sensitive)
2097
{
×
2098
    BinaryData data(right);
×
2099
    if (case_sensitive)
×
2100
        return create<S>(data, left);
×
2101
    else
×
2102
        return create<I>(data, left);
×
2103
}
×
2104

2105
template <class S, class I>
2106
Query binary_compare(const Subexpr2<BinaryData>& left, const Subexpr2<BinaryData>& right, bool case_sensitive)
2107
{
×
2108
    if (case_sensitive)
×
2109
        return make_expression<Compare<S>>(right.clone(), left.clone());
×
2110
    else
×
2111
        return make_expression<Compare<I>>(right.clone(), left.clone());
×
2112
}
×
2113

2114
template <class T, class S, class I>
2115
Query mixed_compare(const Subexpr2<Mixed>& left, T right, bool case_sensitive)
2116
{
54✔
2117
    Mixed data(right);
54✔
2118
    if (case_sensitive)
54✔
2119
        return create<S>(data, left);
30✔
2120
    else
24✔
2121
        return create<I>(data, left);
24✔
2122
}
54✔
2123

2124
template <class S, class I>
2125
Query mixed_compare(const Subexpr2<Mixed>& left, const Subexpr2<Mixed>& right, bool case_sensitive)
2126
{
×
2127
    if (case_sensitive)
×
2128
        return make_expression<Compare<S>>(right.clone(), left.clone());
×
2129
    else
×
2130
        return make_expression<Compare<I>>(right.clone(), left.clone());
×
2131
}
×
2132

2133

2134
// This class is intended to perform queries on the *pointers* of links, contrary to performing queries on *payload*
2135
// in linked-to tables. Queries can be "find first link that points at row X" or "find first null-link". Currently
2136
// only "find first null link" and "find first non-null link" is supported. More will be added later. When we add
2137
// more, I propose to remove the <bool has_links> template argument from this class and instead template it by
2138
// a criteria-class (like the FindNullLinks class below in find_first()) in some generalized fashion.
2139
template <bool has_links>
2140
class UnaryLinkCompare : public Expression {
2141
public:
2142
    UnaryLinkCompare(const LinkMap& lm)
2143
        : m_link_map(lm)
2144
    {
126✔
2145
    }
126✔
2146

2147
    void set_base_table(ConstTableRef table) override
2148
    {
152✔
2149
        m_link_map.set_base_table(table);
152✔
2150
    }
152✔
2151

2152
    void set_cluster(const Cluster* cluster) override
2153
    {
208✔
2154
        m_link_map.set_cluster(cluster);
208✔
2155
    }
208✔
2156

2157
    void collect_dependencies(std::vector<TableKey>& tables) const override
2158
    {
28✔
2159
        m_link_map.collect_dependencies(tables);
28✔
2160
    }
28✔
2161

2162
    // Return main table of query (table on which table->where()... is invoked). Note that this is not the same as
2163
    // any linked-to payload tables
2164
    ConstTableRef get_base_table() const override
2165
    {
126✔
2166
        return m_link_map.get_base_table();
126✔
2167
    }
126✔
2168

2169
    size_t find_first(size_t start, size_t end) const override
2170
    {
1,574✔
2171
        for (; start < end; ++start) {
3,056✔
2172
            bool found_link = false;
2,926✔
2173
            m_link_map.map_links(start, [&](ObjKey) {
2,214✔
2174
                found_link = true;
1,502✔
2175
                return false; // we've found a key, so this can't be a null-link, so exit link harvesting
1,502✔
2176
            });
1,502✔
2177
            if (found_link == has_links)
2,926✔
2178
                return start;
1,444✔
2179
        }
2,926✔
2180

787✔
2181
        return not_found;
852✔
2182
    }
1,574✔
2183

2184
    std::string description(util::serializer::SerialisationState& state) const override
2185
    {
×
2186
        return state.describe_columns(m_link_map, ColKey()) + (has_links ? " != NULL" : " == NULL");
×
2187
    }
×
2188

2189
    std::unique_ptr<Expression> clone() const override
2190
    {
114✔
2191
        return std::unique_ptr<Expression>(new UnaryLinkCompare(*this));
114✔
2192
    }
114✔
2193

2194
private:
2195
    UnaryLinkCompare(const UnaryLinkCompare& other)
2196
        : Expression(other)
2197
        , m_link_map(other.m_link_map)
2198
    {
114✔
2199
    }
114✔
2200

2201
    mutable LinkMap m_link_map;
2202
};
2203

2204
class LinkCount : public Subexpr2<Int> {
2205
public:
2206
    LinkCount(const LinkMap& link_map)
2207
        : m_link_map(link_map)
2208
    {
836✔
2209
        if (m_link_map.get_nb_hops() > 1) {
836✔
2210
            m_column_key = m_link_map.pop_last();
44✔
2211
        }
44✔
2212
    }
836✔
2213
    LinkCount(LinkCount const& other)
2214
        : Subexpr2<Int>(other)
2215
        , m_link_map(other.m_link_map)
2216
        , m_column_key(other.m_column_key)
2217
    {
2,472✔
2218
    }
2,472✔
2219

2220
    std::unique_ptr<Subexpr> clone() const override
2221
    {
2,472✔
2222
        return make_subexpr<LinkCount>(*this);
2,472✔
2223
    }
2,472✔
2224

2225
    ConstTableRef get_base_table() const override
2226
    {
1,260✔
2227
        return m_link_map.get_base_table();
1,260✔
2228
    }
1,260✔
2229

2230
    void set_base_table(ConstTableRef table) override
2231
    {
1,440✔
2232
        m_link_map.set_base_table(table);
1,440✔
2233
    }
1,440✔
2234

2235
    void set_cluster(const Cluster* cluster) override
2236
    {
1,938✔
2237
        m_link_map.set_cluster(cluster);
1,938✔
2238
    }
1,938✔
2239

2240
    void collect_dependencies(std::vector<TableKey>& tables) const override
2241
    {
30✔
2242
        m_link_map.collect_dependencies(tables);
30✔
2243
    }
30✔
2244

2245
    void evaluate(size_t index, ValueBase& destination) override;
2246

2247
    std::string description(util::serializer::SerialisationState& state) const override;
2248

2249
private:
2250
    LinkMap m_link_map;
2251
    ColKey m_column_key;
2252
};
2253

2254
// Gives a count of all backlinks across all columns for the specified row.
2255
// The unused template parameter is a hack to avoid a circular dependency between table.hpp and query_expression.hpp.
2256
template <class>
2257
class BacklinkCount : public Subexpr2<Int> {
2258
public:
2259
    BacklinkCount(const LinkMap& link_map)
2260
        : m_link_map(link_map)
2261
    {
2262
    }
2263
    BacklinkCount(LinkMap&& link_map)
2264
        : m_link_map(std::move(link_map))
2265
    {
2266
    }
2267
    BacklinkCount(ConstTableRef table, std::vector<ColKey> links = {})
2268
        : m_link_map(table, std::move(links))
2269
    {
304✔
2270
    }
304✔
2271
    BacklinkCount(BacklinkCount const& other)
2272
        : Subexpr2<Int>(other)
2273
        , m_link_map(other.m_link_map)
2274
    {
568✔
2275
    }
568✔
2276

2277
    std::unique_ptr<Subexpr> clone() const override
2278
    {
568✔
2279
        return make_subexpr<BacklinkCount<Int>>(*this);
568✔
2280
    }
568✔
2281

2282
    ConstTableRef get_base_table() const override
2283
    {
264✔
2284
        return m_link_map.get_base_table();
264✔
2285
    }
264✔
2286

2287
    void set_base_table(ConstTableRef table) override
2288
    {
264✔
2289
        m_link_map.set_base_table(table);
264✔
2290
    }
264✔
2291

2292
    void set_cluster(const Cluster* cluster) override
2293
    {
264✔
2294
        if (m_link_map.has_links()) {
264✔
2295
            m_link_map.set_cluster(cluster);
88✔
2296
        }
88✔
2297
        else {
176✔
2298
            m_cluster = cluster;
176✔
2299
        }
176✔
2300
    }
264✔
2301

2302
    void collect_dependencies(std::vector<TableKey>& tables) const override
2303
    {
×
2304
        m_link_map.collect_dependencies(tables);
×
2305
    }
×
2306

2307
    void evaluate(size_t index, ValueBase& destination) override
2308
    {
872✔
2309
        size_t count;
872✔
2310
        if (m_link_map.has_links()) {
872✔
2311
            count = m_link_map.count_all_backlinks(index);
184✔
2312
        }
184✔
2313
        else {
688✔
2314
            const Obj obj = m_link_map.get_base_table()->get_object(m_cluster->get_real_key(index));
688✔
2315
            count = obj.get_backlink_count();
688✔
2316
        }
688✔
2317
        destination = Value<int64_t>(count);
872✔
2318
    }
872✔
2319

2320
    std::string description(util::serializer::SerialisationState& state) const override
2321
    {
132✔
2322
        std::string s;
132✔
2323
        if (m_link_map.links_exist()) {
132✔
2324
            s += state.describe_columns(m_link_map, ColKey()) + util::serializer::value_separator;
44✔
2325
        }
44✔
2326
        s += "@links.@count";
132✔
2327
        return s;
132✔
2328
    }
132✔
2329

2330
private:
2331
    const Cluster* m_cluster = nullptr;
2332
    LinkMap m_link_map;
2333
};
2334

2335
#if REALM_ENABLE_GEOSPATIAL
2336
class GeoWithinCompare : public Expression {
2337
public:
2338
    GeoWithinCompare(const LinkMap& lm, Geospatial&& bounds, util::Optional<ExpressionComparisonType> comp_type)
2339
        : m_link_map(lm)
2340
        , m_bounds(std::move(bounds))
2341
        , m_region(m_bounds)
2342
        , m_comp_type(comp_type)
2343
    {
392✔
2344
        Status status = m_region.get_conversion_status();
392✔
2345
        if (!status.is_ok()) {
392✔
2346
            throw InvalidArgument(status.code(),
32✔
2347
                                  util::format("Invalid region in GEOWITHIN query for parameter '%1': '%2'", m_bounds,
32✔
2348
                                               status.reason()));
32✔
2349
        }
32✔
2350
    }
392✔
2351

2352
    GeoWithinCompare(const GeoWithinCompare& other)
2353
        : m_link_map(other.m_link_map)
2354
        , m_bounds(other.m_bounds)
2355
        , m_region(m_bounds)
2356
        , m_comp_type(other.m_comp_type)
2357
    {
184✔
2358
    }
184✔
2359

2360
    void set_base_table(ConstTableRef table) override
2361
    {
360✔
2362
        m_link_map.set_base_table(table);
360✔
2363
        m_coords_col = m_link_map.get_target_table()->get_column_key(Geospatial::c_geo_point_coords_col_name);
360✔
2364
        m_type_col = m_link_map.get_target_table()->get_column_key(Geospatial::c_geo_point_type_col_name);
360✔
2365
        if (!m_coords_col || !m_type_col || !m_coords_col.is_list() ||
360✔
2366
            m_coords_col.get_type() != ColumnType(ColumnType::Type::Double) ||
358✔
2367
            m_type_col.get_type() != ColumnType(ColumnType::Type::String) || m_type_col.is_collection()) {
358✔
2368
            util::serializer::SerialisationState none;
4✔
2369
            throw std::runtime_error(util::format(
4✔
2370
                "Query '%1' links to data in the wrong format for a geoWithin query", this->description(none)));
4✔
2371
        }
4✔
2372
        if (!m_link_map.get_target_table()->is_embedded()) {
356✔
2373
            throw std::runtime_error(util::format(
4✔
2374
                "A GEOWITHIN query can only operate on a link to an embedded class but '%1' is at the top level",
4✔
2375
                m_link_map.get_target_table()->get_class_name()));
4✔
2376
        }
4✔
2377
    }
356✔
2378

2379
    void set_cluster(const Cluster* cluster) override
2380
    {
340✔
2381
        m_link_map.set_cluster(cluster);
340✔
2382
    }
340✔
2383

2384
    void collect_dependencies(std::vector<TableKey>& tables) const override
2385
    {
28✔
2386
        m_link_map.collect_dependencies(tables);
28✔
2387
    }
28✔
2388

2389
    // Return main table of query (table on which table->where()... is invoked). Note that this is not the same as
2390
    // any linked-to payload tables
2391
    ConstTableRef get_base_table() const override
2392
    {
360✔
2393
        return m_link_map.get_base_table();
360✔
2394
    }
360✔
2395

2396
    size_t find_first(size_t start, size_t end) const override
2397
    {
896✔
2398
        auto table = m_link_map.get_target_table();
896✔
2399

448✔
2400
        while (start < end) {
2,664✔
2401
            bool found = false;
2,392✔
2402
            switch (m_comp_type.value_or(ExpressionComparisonType::Any)) {
2,392✔
2403
                case ExpressionComparisonType::Any: {
2,120✔
2404
                    m_link_map.map_links(start, [&](ObjKey key) {
2,156✔
2405
                        found = m_region.contains(
2,156✔
2406
                            Geospatial::point_from_obj(table->get_object(key), m_type_col, m_coords_col));
2,156✔
2407
                        return !found; // keep searching if not found, stop searching on first match
2,156✔
2408
                    });
2,156✔
2409
                    if (found)
2,120✔
2410
                        return start;
520✔
2411
                    break;
1,600✔
2412
                }
1,600✔
2413
                case ExpressionComparisonType::All: {
868✔
2414
                    m_link_map.map_links(start, [&](ObjKey key) {
128✔
2415
                        found = m_region.contains(
120✔
2416
                            Geospatial::point_from_obj(table->get_object(key), m_type_col, m_coords_col));
120✔
2417
                        return found; // keep searching until one the first non-match
120✔
2418
                    });
120✔
2419
                    if (found) // all matched
136✔
2420
                        return start;
4✔
2421
                    break;
132✔
2422
                }
132✔
2423
                case ExpressionComparisonType::None: {
136✔
2424
                    m_link_map.map_links(start, [&](ObjKey key) {
312✔
2425
                        found = m_region.contains(
312✔
2426
                            Geospatial::point_from_obj(table->get_object(key), m_type_col, m_coords_col));
312✔
2427
                        return !found; // keep searching until the first match
312✔
2428
                    });
312✔
2429
                    if (!found) // none matched
136✔
2430
                        return start;
100✔
2431
                    break;
36✔
2432
                }
36✔
2433
            }
1,768✔
2434
            start++;
1,768✔
2435
        }
1,768✔
2436

448✔
2437
        return not_found;
584✔
2438
    }
896✔
2439

2440
    std::string description(util::serializer::SerialisationState& state) const override
2441
    {
140✔
2442
        return state.describe_expression_type(m_comp_type) + state.describe_columns(m_link_map, ColKey()) +
140✔
2443
               " GEOWITHIN " + util::serializer::print_value(m_bounds);
140✔
2444
    }
140✔
2445

2446
    std::unique_ptr<Expression> clone() const override
2447
    {
184✔
2448
        return std::unique_ptr<Expression>(new GeoWithinCompare(*this));
184✔
2449
    }
184✔
2450

2451
private:
2452
    LinkMap m_link_map;
2453
    Geospatial m_bounds;
2454
    GeoRegion m_region;
2455
    ColKey m_type_col;
2456
    ColKey m_coords_col;
2457
    util::Optional<ExpressionComparisonType> m_comp_type;
2458
};
2459
#endif
2460

2461
template <class T, class TExpr>
2462
class SizeOperator : public Subexpr2<Int> {
2463
public:
2464
    SizeOperator(std::unique_ptr<TExpr> left)
2465
        : m_expr(std::move(left))
2466
    {
5,912✔
2467
    }
5,912✔
2468

2469
    SizeOperator(const SizeOperator& other)
2470
        : m_expr(other.m_expr->clone())
2471
    {
11,560✔
2472
    }
11,560✔
2473

2474
    // See comment in base class
2475
    void set_base_table(ConstTableRef table) override
2476
    {
6,212✔
2477
        m_expr->set_base_table(table);
6,212✔
2478
    }
6,212✔
2479

2480
    void set_cluster(const Cluster* cluster) override
2481
    {
6,608✔
2482
        m_expr->set_cluster(cluster);
6,608✔
2483
    }
6,608✔
2484

2485
    // Recursively fetch tables of columns in expression tree. Used when user first builds a stand-alone expression
2486
    // and binds it to a Query at a later time
2487
    ConstTableRef get_base_table() const override
2488
    {
5,912✔
2489
        return m_expr->get_base_table();
5,912✔
2490
    }
5,912✔
2491

2492
    // destination = operator(left)
2493
    void evaluate(size_t index, ValueBase& destination) override
2494
    {
72,392✔
2495
        Value<T> v;
72,392✔
2496
        m_expr->evaluate(index, v);
72,392✔
2497

36,196✔
2498
        size_t sz = v.size();
72,392✔
2499
        destination.init(v.m_from_list, sz);
72,392✔
2500

36,196✔
2501
        for (size_t i = 0; i < sz; i++) {
144,940✔
2502
            auto elem = v[i].template get<T>();
72,548✔
2503
            if constexpr (std::is_same_v<T, int64_t>) {
72,548✔
2504
                // This is the size of a list
16,962✔
2505
                destination.set(i, elem);
45,930✔
2506
            }
45,930✔
2507
            else {
33,924✔
2508
                if (!elem) {
33,924✔
2509
                    destination.set_null(i);
19,432✔
2510
                }
19,432✔
2511
                else {
33,804✔
2512
                    destination.set(i, int64_t(elem.size()));
33,804✔
2513
                }
33,804✔
2514
            }
33,924✔
2515
        }
72,548✔
2516
    }
72,392✔
2517

2518
    std::string description(util::serializer::SerialisationState& state) const override
2519
    {
2,320✔
2520
        if (m_expr) {
2,320✔
2521
            return m_expr->description(state) + util::serializer::value_separator + "@size";
2,320✔
2522
        }
2,320✔
2523
        return "@size";
×
2524
    }
×
2525

2526
    std::unique_ptr<Subexpr> clone() const override
2527
    {
11,560✔
2528
        return std::unique_ptr<Subexpr>(new SizeOperator(*this));
11,560✔
2529
    }
11,560✔
2530

2531
private:
2532
    std::unique_ptr<TExpr> m_expr;
2533
};
2534

2535
template <class T>
2536
class TypeOfValueOperator : public Subexpr2<TypeOfValue> {
2537
public:
2538
    TypeOfValueOperator(std::unique_ptr<Subexpr> left)
2539
        : m_expr(std::move(left))
2540
    {
608✔
2541
    }
608✔
2542

2543
    TypeOfValueOperator(const TypeOfValueOperator& other)
2544
        : m_expr(other.m_expr->clone())
2545
    {
1,200✔
2546
    }
1,200✔
2547

2548
    util::Optional<ExpressionComparisonType> get_comparison_type() const override
2549
    {
16,024✔
2550
        return m_expr->get_comparison_type();
16,024✔
2551
    }
16,024✔
2552

2553
    // See comment in base class
2554
    void set_base_table(ConstTableRef table) override
2555
    {
572✔
2556
        m_expr->set_base_table(table);
572✔
2557
    }
572✔
2558

2559
    void set_cluster(const Cluster* cluster) override
2560
    {
1,364✔
2561
        m_expr->set_cluster(cluster);
1,364✔
2562
    }
1,364✔
2563

2564
    // Recursively fetch tables of columns in expression tree. Used when user first builds a stand-alone expression
2565
    // and binds it to a Query at a later time
2566
    ConstTableRef get_base_table() const override
2567
    {
572✔
2568
        return m_expr->get_base_table();
572✔
2569
    }
572✔
2570

2571
    // destination = operator(left)
2572
    void evaluate(size_t index, ValueBase& destination) override
2573
    {
44,340✔
2574
        Value<T> v;
44,340✔
2575
        m_expr->evaluate(index, v);
44,340✔
2576

22,170✔
2577
        size_t sz = v.size();
44,340✔
2578
        destination.init(v.m_from_list, sz);
44,340✔
2579

22,170✔
2580
        for (size_t i = 0; i < sz; i++) {
91,228✔
2581
            auto elem = v[i].template get<T>();
46,888✔
2582
            destination.set(i, TypeOfValue(elem));
46,888✔
2583
        }
46,888✔
2584
    }
44,340✔
2585

2586
    std::string description(util::serializer::SerialisationState& state) const override
2587
    {
264✔
2588
        if (m_expr) {
264✔
2589
            return m_expr->description(state) + util::serializer::value_separator + "@type";
264✔
2590
        }
264✔
2591
        return "@type";
×
2592
    }
×
2593

2594
    std::unique_ptr<Subexpr> clone() const override
2595
    {
1,200✔
2596
        return std::unique_ptr<Subexpr>(new TypeOfValueOperator(*this));
1,200✔
2597
    }
1,200✔
2598

2599
private:
2600
    std::unique_ptr<Subexpr> m_expr;
2601
};
2602

2603
class KeyValue : public Subexpr2<Link> {
2604
public:
2605
    KeyValue(ObjKey key)
2606
        : m_key(key)
2607
    {
64✔
2608
    }
64✔
2609

2610
    void set_base_table(ConstTableRef) override {}
216✔
2611

2612
    ConstTableRef get_base_table() const override
2613
    {
64✔
2614
        return nullptr;
64✔
2615
    }
64✔
2616

2617
    void evaluate(size_t, ValueBase& destination) override
2618
    {
624✔
2619
        destination = Value<ObjKey>(m_key);
624✔
2620
    }
624✔
2621

2622
    std::string description(util::serializer::SerialisationState&) const override
2623
    {
40✔
2624
        return util::serializer::print_value(m_key);
40✔
2625
    }
40✔
2626

2627
    std::unique_ptr<Subexpr> clone() const override
2628
    {
256✔
2629
        return std::unique_ptr<Subexpr>(new KeyValue(*this));
256✔
2630
    }
256✔
2631

2632
private:
2633
    KeyValue(const KeyValue& source)
2634
        : m_key(source.m_key)
2635
    {
256✔
2636
    }
256✔
2637

2638
    ObjKey m_key;
2639
};
2640

2641
template <typename T>
2642
class SubColumns;
2643

2644
// This is for LinkList and BackLink too since they're declared as typedefs of Link.
2645
template <>
2646
class Columns<Link> : public Subexpr2<Link> {
2647
public:
2648
    Columns(const Columns& other)
2649
        : Subexpr2<Link>(other)
2650
        , m_link_map(other.m_link_map)
2651
        , m_comparison_type(other.m_comparison_type)
2652
        , m_is_list(other.m_is_list)
2653
    {
666✔
2654
    }
666✔
2655

2656
    Columns(ColKey column_key, ConstTableRef table, const std::vector<ColKey>& links = {},
2657
            util::Optional<ExpressionComparisonType> type = util::none)
2658
        : m_link_map(table, links)
2659
        , m_comparison_type(type)
2660
        , m_is_list(column_key.is_list())
2661
    {
5,952✔
2662
    }
5,952✔
2663

2664
    Query is_null()
2665
    {
76✔
2666
        if (m_link_map.get_nb_hops() > 1)
76✔
2667
            throw Exception(ErrorCodes::InvalidQuery, "Combining link() and is_null() is currently not supported");
2✔
2668
        // Todo, it may be useful to support the above, but we would need to figure out an intuitive behaviour
37✔
2669
        return make_expression<UnaryLinkCompare<false>>(m_link_map);
74✔
2670
    }
74✔
2671

2672
    Query is_not_null()
2673
    {
52✔
2674
        if (m_link_map.get_nb_hops() > 1)
52✔
2675
            throw Exception(ErrorCodes::InvalidQuery,
×
2676
                            "Combining link() and is_not_null() is currently not supported");
×
2677
        // Todo, it may be useful to support the above, but we would need to figure out an intuitive behaviour
26✔
2678
        return make_expression<UnaryLinkCompare<true>>(m_link_map);
52✔
2679
    }
52✔
2680

2681
#if REALM_ENABLE_GEOSPATIAL
2682
    Query geo_within(Geospatial bounds) const
2683
    {
392✔
2684
        return make_expression<GeoWithinCompare>(m_link_map, std::move(bounds), m_comparison_type);
392✔
2685
    }
392✔
2686
#endif
2687

2688
    LinkCount count() const
2689
    {
836✔
2690
        return LinkCount(m_link_map);
836✔
2691
    }
836✔
2692

2693
    template <class T>
2694
    BacklinkCount<T> backlink_count() const
2695
    {
2696
        return BacklinkCount<T>(m_link_map);
2697
    }
2698

2699
    template <typename C>
2700
    SubColumns<C> column(ColKey column_key) const
2701
    {
2,964✔
2702
        // no need to pass along m_comparison_type because the only operations supported from
1,482✔
2703
        // the subsequent SubColumns are aggregate operations such as sum, min, max, avg where
1,482✔
2704
        // having
1,482✔
2705
        REALM_ASSERT_DEBUG(!m_comparison_type);
2,964✔
2706
        return SubColumns<C>(Columns<C>(column_key, m_link_map.get_target_table()), m_link_map);
2,964✔
2707
    }
2,964✔
2708

2709
    const LinkMap& link_map() const
2710
    {
3,168✔
2711
        return m_link_map;
3,168✔
2712
    }
3,168✔
2713

2714
    DataType get_type() const override
2715
    {
1,452✔
2716
        return type_Link;
1,452✔
2717
    }
1,452✔
2718

2719
    ConstTableRef get_target_table() const override
2720
    {
156✔
2721
        return link_map().get_target_table();
156✔
2722
    }
156✔
2723

2724
    bool has_multiple_values() const override
2725
    {
618✔
2726
        return m_is_list || !m_link_map.only_unary_links();
618✔
2727
    }
618✔
2728

2729
    ConstTableRef get_base_table() const override
2730
    {
1,092✔
2731
        return m_link_map.get_base_table();
1,092✔
2732
    }
1,092✔
2733

2734
    void set_base_table(ConstTableRef table) override
2735
    {
504✔
2736
        m_link_map.set_base_table(table);
504✔
2737
    }
504✔
2738

2739
    void set_cluster(const Cluster* cluster) override
2740
    {
318✔
2741
        REALM_ASSERT(m_link_map.has_links());
318✔
2742
        m_link_map.set_cluster(cluster);
318✔
2743
    }
318✔
2744

2745
    void collect_dependencies(std::vector<TableKey>& tables) const override
2746
    {
276✔
2747
        m_link_map.collect_dependencies(tables);
276✔
2748
    }
276✔
2749

2750
    std::string description(util::serializer::SerialisationState& state) const override
2751
    {
186✔
2752
        return state.describe_expression_type(m_comparison_type) + state.describe_columns(m_link_map, ColKey());
186✔
2753
    }
186✔
2754

2755
    util::Optional<ExpressionComparisonType> get_comparison_type() const override
2756
    {
1,584✔
2757
        return m_comparison_type;
1,584✔
2758
    }
1,584✔
2759

2760
    std::unique_ptr<Subexpr> clone() const override
2761
    {
666✔
2762
        return std::unique_ptr<Subexpr>(new Columns<Link>(*this));
666✔
2763
    }
666✔
2764

2765
    void evaluate(size_t index, ValueBase& destination) override;
2766

2767
private:
2768
    LinkMap m_link_map;
2769
    util::Optional<ExpressionComparisonType> m_comparison_type;
2770
    bool m_is_list;
2771
    friend class Table;
2772
    friend class LinkChain;
2773
};
2774

2775
template <typename T>
2776
class ListColumns;
2777
template <typename T, typename Operation>
2778
class CollectionColumnAggregate;
2779
namespace aggregate_operations {
2780
template <typename T>
2781
class Minimum;
2782
template <typename T>
2783
class Maximum;
2784
template <typename T>
2785
class Sum;
2786
template <typename T>
2787
class Average;
2788
} // namespace aggregate_operations
2789

2790
class ColumnListBase {
2791
public:
2792
    ColumnListBase(ColKey column_key, ConstTableRef table, const std::vector<ColKey>& links,
2793
                   util::Optional<ExpressionComparisonType> type = util::none)
2794
        : m_column_key(column_key)
2795
        , m_link_map(table, links)
2796
        , m_comparison_type(type)
2797
    {
20,856✔
2798
    }
20,856✔
2799

2800
    ColumnListBase(const ColumnListBase& other)
2801
        : m_column_key(other.m_column_key)
2802
        , m_link_map(other.m_link_map)
2803
        , m_comparison_type(other.m_comparison_type)
2804
    {
70,434✔
2805
    }
70,434✔
2806

2807
    void set_cluster(const Cluster* cluster);
2808

2809
    void get_lists(size_t index, Value<int64_t>& destination, size_t nb_elements);
2810

2811
    std::string description(util::serializer::SerialisationState& state) const
2812
    {
11,832✔
2813
        return state.describe_expression_type(m_comparison_type) + state.describe_columns(m_link_map, m_column_key);
11,832✔
2814
    }
11,832✔
2815

2816
    bool links_exist() const
2817
    {
34,470✔
2818
        return m_link_map.has_links();
34,470✔
2819
    }
34,470✔
2820

2821
    virtual SizeOperator<int64_t> size() = 0;
2822
    virtual std::unique_ptr<Subexpr> get_element_length() = 0;
2823
    virtual std::unique_ptr<Subexpr> max_of() = 0;
2824
    virtual std::unique_ptr<Subexpr> min_of() = 0;
2825
    virtual std::unique_ptr<Subexpr> sum_of() = 0;
2826
    virtual std::unique_ptr<Subexpr> avg_of() = 0;
2827

2828
    mutable ColKey m_column_key;
2829
    LinkMap m_link_map;
2830
    std::optional<ArrayInteger> m_leaf;
2831
    std::optional<ExpressionComparisonType> m_comparison_type;
2832
};
2833

2834
template <typename>
2835
class ColumnListSize;
2836

2837
template <typename T>
2838
class ColumnListElementLength;
2839

2840
template <typename T>
2841
class ColumnsCollection : public Subexpr2<T>, public ColumnListBase {
2842
public:
2843
    ColumnsCollection(ColKey column_key, ConstTableRef table, const std::vector<ColKey>& links = {},
2844
                      util::Optional<ExpressionComparisonType> type = util::none)
2845
        : ColumnListBase(column_key, table, links, type)
2846
        , m_is_nullable_storage(this->m_column_key.get_attrs().test(col_attr_Nullable))
2847
    {
20,840✔
2848
    }
20,840✔
2849

2850
    ColumnsCollection(const ColumnsCollection& other)
2851
        : Subexpr2<T>(other)
2852
        , ColumnListBase(other)
2853
        , m_is_nullable_storage(this->m_column_key.get_attrs().test(col_attr_Nullable))
2854
    {
50,706✔
2855
    }
50,706✔
2856

2857
    bool has_multiple_values() const override
2858
    {
164✔
2859
        return true;
164✔
2860
    }
164✔
2861

2862
    ConstTableRef get_base_table() const final
2863
    {
53,390✔
2864
        return m_link_map.get_base_table();
53,390✔
2865
    }
53,390✔
2866
    ConstTableRef get_target_table() const override
2867
    {
3,844✔
2868
        return m_link_map.get_target_table()->get_opposite_table(m_column_key);
3,844✔
2869
    }
3,844✔
2870

2871
    Allocator& get_alloc() const
2872
    {
1,350,836✔
2873
        return m_link_map.get_target_table()->get_alloc();
1,350,836✔
2874
    }
1,350,836✔
2875

2876
    void set_base_table(ConstTableRef table) final
2877
    {
22,100✔
2878
        m_link_map.set_base_table(table);
22,100✔
2879
    }
22,100✔
2880

2881
    void set_cluster(const Cluster* cluster) final
2882
    {
26,996✔
2883
        ColumnListBase::set_cluster(cluster);
26,996✔
2884
    }
26,996✔
2885

2886
    void collect_dependencies(std::vector<TableKey>& tables) const final
2887
    {
712✔
2888
        m_link_map.collect_dependencies(tables);
712✔
2889
    }
712✔
2890

2891
    void evaluate(size_t index, ValueBase& destination) override
2892
    {
92,030✔
2893
        if constexpr (realm::is_any_v<T, ObjectId, Int, Bool, UUID>) {
92,030✔
2894
            if (m_is_nullable_storage) {
42,754✔
2895
                evaluate<util::Optional<T>>(index, destination);
10,570✔
2896
                return;
10,570✔
2897
            }
10,570✔
2898
        }
35,970✔
2899
        evaluate<T>(index, destination);
35,970✔
2900
    }
35,970✔
2901

2902
    std::string description(util::serializer::SerialisationState& state) const override
2903
    {
8,306✔
2904
        return ColumnListBase::description(state);
8,306✔
2905
    }
8,306✔
2906

2907
    util::Optional<ExpressionComparisonType> get_comparison_type() const final
2908
    {
57,494✔
2909
        return ColumnListBase::m_comparison_type;
57,494✔
2910
    }
57,494✔
2911

2912
    SizeOperator<int64_t> size() override;
2913

2914
    ColumnListElementLength<T> element_lengths() const
2915
    {
1,636✔
2916
        return {*this};
1,636✔
2917
    }
1,636✔
2918

2919
    TypeOfValueOperator<T> type_of_value()
2920
    {
128✔
2921
        return TypeOfValueOperator<T>(this->clone());
128✔
2922
    }
128✔
2923

2924
    CollectionColumnAggregate<T, aggregate_operations::Minimum<T>> min() const
2925
    {
1,420✔
2926
        return {*this};
1,420✔
2927
    }
1,420✔
2928

2929
    CollectionColumnAggregate<T, aggregate_operations::Maximum<T>> max() const
2930
    {
1,460✔
2931
        return {*this};
1,460✔
2932
    }
1,460✔
2933

2934
    CollectionColumnAggregate<T, aggregate_operations::Sum<T>> sum() const
2935
    {
1,444✔
2936
        return {*this};
1,444✔
2937
    }
1,444✔
2938

2939
    CollectionColumnAggregate<T, aggregate_operations::Average<T>> average() const
2940
    {
1,256✔
2941
        return {*this};
1,256✔
2942
    }
1,256✔
2943

2944
    std::unique_ptr<Subexpr> max_of() override
2945
    {
792✔
2946
        if constexpr (realm::is_any_v<T, Int, Float, Double, Decimal128, Mixed>) {
792✔
2947
            return max().clone();
208✔
2948
        }
208✔
2949
        else {
208✔
2950
            return {};
208✔
2951
        }
208✔
2952
    }
792✔
2953
    std::unique_ptr<Subexpr> min_of() override
2954
    {
752✔
2955
        if constexpr (realm::is_any_v<T, Int, Float, Double, Decimal128, Mixed>) {
752✔
2956
            return min().clone();
192✔
2957
        }
192✔
2958
        else {
192✔
2959
            return {};
192✔
2960
        }
192✔
2961
    }
752✔
2962
    std::unique_ptr<Subexpr> sum_of() override
2963
    {
980✔
2964
        if constexpr (realm::is_any_v<T, Int, Float, Double, Decimal128, Mixed>) {
980✔
2965
            return sum().clone();
228✔
2966
        }
228✔
2967
        else {
228✔
2968
            return {};
228✔
2969
        }
228✔
2970
    }
980✔
2971
    std::unique_ptr<Subexpr> avg_of() override
2972
    {
820✔
2973
        if constexpr (realm::is_any_v<T, Int, Float, Double, Decimal128, Mixed>) {
820✔
2974
            return average().clone();
196✔
2975
        }
196✔
2976
        else {
196✔
2977
            return {};
196✔
2978
        }
196✔
2979
    }
820✔
2980

2981
    std::unique_ptr<Subexpr> get_element_length() override
2982
    {
1,700✔
2983
        if constexpr (realm::is_any_v<T, StringData, BinaryData, Mixed>) {
1,700✔
2984
            return element_lengths().clone();
76✔
2985
        }
76✔
2986
        else {
76✔
2987
            return {};
76✔
2988
        }
76✔
2989
    }
1,700✔
2990

2991
    std::unique_ptr<Subexpr> clone() const override
2992
    {
×
2993
        return std::unique_ptr<Subexpr>(new ColumnsCollection(*this));
×
2994
    }
×
2995
    const bool m_is_nullable_storage;
2996

2997
private:
2998
    template <typename StorageType>
2999
    void evaluate(size_t index, ValueBase& destination)
3000
    {
92,030✔
3001
        Allocator& alloc = get_alloc();
92,030✔
3002
        Value<int64_t> list_refs;
92,030✔
3003
        get_lists(index, list_refs, 1);
92,030✔
3004
        const bool is_from_list = true;
92,030✔
3005

46,015✔
3006
        std::vector<StorageType> values;
92,030✔
3007
        for (auto&& i : list_refs) {
93,442!
3008
            ref_type list_ref = to_ref(i.get_int());
93,442✔
3009
            if (list_ref) {
93,442!
3010
                BPlusTree<StorageType> list(alloc);
67,390✔
3011
                list.init_from_ref(list_ref);
67,390✔
3012
                size_t s = list.size();
67,390✔
3013
                for (size_t j = 0; j < s; j++) {
204,174!
3014
                    values.push_back(list.get(j));
136,784✔
3015
                }
136,784✔
3016
            }
67,390✔
3017
        }
93,442✔
3018
        destination.init(is_from_list, values.size());
92,030✔
3019
        destination.set(values.begin(), values.end());
92,030✔
3020
    }
92,030✔
3021
};
3022

3023
template <typename T>
3024
class Columns<Lst<T>> : public ColumnsCollection<T> {
3025
public:
3026
    using ColumnsCollection<T>::ColumnsCollection;
3027
    std::unique_ptr<Subexpr> clone() const override
3028
    {
9,084✔
3029
        return make_subexpr<Columns<Lst<T>>>(*this);
9,084✔
3030
    }
9,084✔
3031
    friend class Table;
3032
    friend class LinkChain;
3033
};
3034

3035
template <typename T>
3036
class Columns<Set<T>> : public ColumnsCollection<T> {
3037
public:
3038
    using ColumnsCollection<T>::ColumnsCollection;
3039
    std::unique_ptr<Subexpr> clone() const override
3040
    {
1,792✔
3041
        return make_subexpr<Columns<Set<T>>>(*this);
1,792✔
3042
    }
1,792✔
3043
};
3044

3045

3046
template <>
3047
class Columns<LnkLst> : public Columns<Lst<ObjKey>> {
3048
public:
3049
    using Columns<Lst<ObjKey>>::Columns;
3050

3051
    std::unique_ptr<Subexpr> clone() const override
3052
    {
×
3053
        return make_subexpr<Columns<LnkLst>>(*this);
×
3054
    }
×
3055
};
3056

3057
template <>
3058
class Columns<LnkSet> : public Columns<Set<ObjKey>> {
3059
public:
3060
    using Columns<Set<ObjKey>>::Columns;
3061

3062
    std::unique_ptr<Subexpr> clone() const override
3063
    {
×
3064
        return make_subexpr<Columns<LnkSet>>(*this);
×
3065
    }
×
3066
};
3067

3068
// Returns the keys
3069
class ColumnDictionaryKeys;
3070

3071
// Returns the values of a given key
3072
class ColumnDictionaryKey;
3073

3074
// Returns the values
3075
template <>
3076
class Columns<Dictionary> : public ColumnsCollection<Mixed> {
3077
public:
3078
    Columns(ColKey column, ConstTableRef table, std::vector<ColKey> links = {},
3079
            util::Optional<ExpressionComparisonType> type = util::none)
3080
        : ColumnsCollection<Mixed>(column, table, std::move(links), type)
3081
    {
1,364✔
3082
        m_key_type = m_link_map.get_target_table()->get_dictionary_key_type(column);
1,364✔
3083
    }
1,364✔
3084

3085
    DataType get_key_type() const
3086
    {
78✔
3087
        return m_key_type;
78✔
3088
    }
78✔
3089

3090
    ColumnDictionaryKey key(const Mixed& key_value);
3091
    ColumnDictionaryKeys keys();
3092

3093
    SizeOperator<int64_t> size() override;
3094
    std::unique_ptr<Subexpr> get_element_length() override
3095
    {
×
3096
        // Not supported for Dictionary
3097
        return {};
×
3098
    }
×
3099

3100
    void evaluate(size_t index, ValueBase& destination) override;
3101

3102
    std::unique_ptr<Subexpr> clone() const override
3103
    {
198✔
3104
        return make_subexpr<Columns<Dictionary>>(*this);
198✔
3105
    }
198✔
3106

3107
    Columns(Columns const& other)
3108
        : ColumnsCollection<Mixed>(other)
3109
        , m_key_type(other.m_key_type)
3110
    {
1,926✔
3111
    }
1,926✔
3112

3113
protected:
3114
    DataType m_key_type;
3115
};
3116

3117
class ColumnDictionaryKey : public Columns<Dictionary> {
3118
public:
3119
    ColumnDictionaryKey(Mixed key_value, const Columns<Dictionary>& dict)
3120
        : Columns<Dictionary>(dict)
3121
    {
762✔
3122
        init_key(key_value);
762✔
3123
    }
762✔
3124

3125
    ColumnDictionaryKey& property(const std::string& prop)
3126
    {
8✔
3127
        m_prop_list.push_back(prop);
8✔
3128
        return *this;
8✔
3129
    }
8✔
3130

3131
    void evaluate(size_t index, ValueBase& destination) override;
3132

3133
    std::string description(util::serializer::SerialisationState& state) const override
3134
    {
60✔
3135
        std::ostringstream ostr;
60✔
3136
        ostr << m_key;
60✔
3137
        return ColumnListBase::description(state) + '[' + ostr.str() + ']';
60✔
3138
    }
60✔
3139

3140
    std::unique_ptr<Subexpr> clone() const override
3141
    {
930✔
3142
        return std::unique_ptr<Subexpr>(new ColumnDictionaryKey(*this));
930✔
3143
    }
930✔
3144

3145
    ColumnDictionaryKey(ColumnDictionaryKey const& other)
3146
        : Columns<Dictionary>(other)
3147
        , m_prop_list(other.m_prop_list)
3148
    {
930✔
3149
        init_key(other.m_key);
930✔
3150
    }
930✔
3151

3152
private:
3153
    Mixed m_key;
3154
    std::string m_buffer;
3155
    std::vector<std::string> m_prop_list;
3156

3157
    void init_key(Mixed key_value);
3158
};
3159

3160
// Returns the keys
3161
class ColumnDictionaryKeys : public Subexpr2<StringData> {
3162
public:
3163
    ColumnDictionaryKeys(const Columns<Dictionary>& dict)
3164
        : m_key_type(dict.get_key_type())
3165
        , m_column_key(dict.m_column_key)
3166
        , m_link_map(dict.m_link_map)
3167
        , m_comparison_type(dict.get_comparison_type())
3168
    {
78✔
3169
        REALM_ASSERT(m_key_type == type_String);
78✔
3170
    }
78✔
3171

3172
    ConstTableRef get_base_table() const final
3173
    {
6,684✔
3174
        return m_link_map.get_base_table();
6,684✔
3175
    }
6,684✔
3176

3177
    void set_base_table(ConstTableRef table) final
3178
    {
78✔
3179
        m_link_map.set_base_table(table);
78✔
3180
    }
78✔
3181

3182
    void collect_dependencies(std::vector<TableKey>& tables) const final
3183
    {
12✔
3184
        m_link_map.collect_dependencies(tables);
12✔
3185
    }
12✔
3186

3187
    util::Optional<ExpressionComparisonType> get_comparison_type() const final
3188
    {
2,094✔
3189
        return m_comparison_type;
2,094✔
3190
    }
2,094✔
3191

3192
    void set_cluster(const Cluster* cluster) override;
3193
    void evaluate(size_t index, ValueBase& destination) override;
3194

3195
    std::string description(util::serializer::SerialisationState& state) const override
3196
    {
30✔
3197
        return state.describe_expression_type(m_comparison_type) + state.describe_columns(m_link_map, m_column_key) +
30✔
3198
               ".@keys";
30✔
3199
    }
30✔
3200

3201
    std::unique_ptr<Subexpr> clone() const override
3202
    {
90✔
3203
        return std::unique_ptr<Subexpr>(new ColumnDictionaryKeys(*this));
90✔
3204
    }
90✔
3205

3206
    ColumnDictionaryKeys(const ColumnDictionaryKeys& other)
3207
        : m_key_type(other.m_key_type)
3208
        , m_column_key(other.m_column_key)
3209
        , m_link_map(other.m_link_map)
3210
        , m_comparison_type(other.m_comparison_type)
3211
    {
90✔
3212
    }
90✔
3213

3214
private:
3215
    DataType m_key_type;
3216
    ColKey m_column_key;
3217
    LinkMap m_link_map;
3218
    std::optional<ExpressionComparisonType> m_comparison_type;
3219
    std::optional<ArrayInteger> m_leaf;
3220
};
3221

3222
template <typename T>
3223
class ColumnListSize : public ColumnsCollection<T> {
3224
public:
3225
    ColumnListSize(const ColumnsCollection<T>& other)
3226
        : ColumnsCollection<T>(other)
3227
    {
3,540✔
3228
    }
3,540✔
3229
    void evaluate(size_t index, ValueBase& destination) override
3230
    {
40,058✔
3231
        if constexpr (realm::is_any_v<T, ObjectId, Int, Bool, UUID>) {
40,058✔
3232
            if (this->m_is_nullable_storage) {
22,898✔
3233
                evaluate<util::Optional<T>>(index, destination);
5,406✔
3234
                return;
5,406✔
3235
            }
5,406✔
3236
        }
19,850✔
3237
        evaluate<T>(index, destination);
19,850✔
3238
    }
19,850✔
3239

3240
    std::unique_ptr<Subexpr> clone() const override
3241
    {
6,950✔
3242
        return std::unique_ptr<Subexpr>(new ColumnListSize<T>(*this));
6,950✔
3243
    }
6,950✔
3244

3245
private:
3246
    template <typename StorageType>
3247
    void evaluate(size_t index, ValueBase& destination)
3248
    {
40,058✔
3249
        Allocator& alloc = ColumnsCollection<T>::get_alloc();
40,058✔
3250
        Value<int64_t> list_refs;
40,058✔
3251
        this->get_lists(index, list_refs, 1);
40,058✔
3252
        destination.init(list_refs.m_from_list, list_refs.size());
40,058✔
3253
        for (size_t i = 0; i < list_refs.size(); i++) {
80,220✔
3254
            ref_type list_ref = to_ref(list_refs[i].get_int());
40,162✔
3255
            if (list_ref) {
40,162✔
3256
                BPlusTree<StorageType> list(alloc);
23,940✔
3257
                list.init_from_ref(list_ref);
23,940✔
3258
                size_t s = list.size();
23,940✔
3259
                destination.set(i, int64_t(s));
23,940✔
3260
            }
23,940✔
3261
            else {
16,222✔
3262
                destination.set(i, 0);
16,222✔
3263
            }
16,222✔
3264
        }
40,162✔
3265
    }
40,058✔
3266
};
3267

3268

3269
template <typename T>
3270
class ColumnListElementLength : public Subexpr2<Int> {
3271
public:
3272
    ColumnListElementLength(const ColumnsCollection<T>& source)
3273
        : m_list(source)
3274
    {
1,636✔
3275
    }
1,636✔
3276
    bool has_multiple_values() const override
3277
    {
120✔
3278
        return true;
120✔
3279
    }
120✔
3280
    void evaluate(size_t index, ValueBase& destination) override
3281
    {
9,820✔
3282
        Allocator& alloc = m_list.get_alloc();
9,820✔
3283
        Value<int64_t> list_refs;
9,820✔
3284
        m_list.get_lists(index, list_refs, 1);
9,820✔
3285
        std::vector<Int> sizes;
9,820✔
3286
        for (size_t i = 0; i < list_refs.size(); i++) {
19,640✔
3287
            ref_type list_ref = to_ref(list_refs[i].get_int());
9,820✔
3288
            if (list_ref) {
9,820✔
3289
                BPlusTree<T> list(alloc);
8,184✔
3290
                list.init_from_ref(list_ref);
8,184✔
3291
                const size_t list_size = list.size();
8,184✔
3292
                sizes.reserve(sizes.size() + list_size);
8,184✔
3293
                for (size_t j = 0; j < list_size; j++) {
19,176✔
3294
                    if constexpr (std::is_same_v<T, Mixed>) {
10,992✔
3295
                        Mixed v = list.get(j);
10,512✔
3296
                        if (!v.is_null()) {
5,616✔
3297
                            if (v.get_type() == type_String) {
600✔
3298
                                sizes.push_back(v.get_string().size());
120✔
3299
                            }
120✔
3300
                            else if (v.get_type() == type_Binary) {
480✔
3301
                                sizes.push_back(v.get_binary().size());
24✔
3302
                            }
24✔
3303
                        }
600✔
3304
                    }
720✔
3305
                    else {
10,512✔
3306
                        sizes.push_back(list.get(j).size());
10,512✔
3307
                    }
10,512✔
3308
                }
10,992✔
3309
            }
8,184✔
3310
        }
9,820✔
3311
        constexpr bool is_from_list = true;
9,820✔
3312
        destination.init(is_from_list, sizes.size());
9,820✔
3313
        destination.set(sizes.begin(), sizes.end());
9,820✔
3314
    }
9,820✔
3315
    ConstTableRef get_base_table() const override
3316
    {
1,636✔
3317
        return m_list.get_base_table();
1,636✔
3318
    }
1,636✔
3319

3320
    void set_base_table(ConstTableRef table) override
3321
    {
1,636✔
3322
        m_list.set_base_table(table);
1,636✔
3323
    }
1,636✔
3324

3325
    void set_cluster(const Cluster* cluster) override
3326
    {
1,636✔
3327
        m_list.set_cluster(cluster);
1,636✔
3328
    }
1,636✔
3329

3330
    void collect_dependencies(std::vector<TableKey>& tables) const override
3331
    {
×
3332
        m_list.collect_dependencies(tables);
×
3333
    }
×
3334

3335
    std::unique_ptr<Subexpr> clone() const override
3336
    {
3,272✔
3337
        return std::unique_ptr<Subexpr>(new ColumnListElementLength<T>(*this));
3,272✔
3338
    }
3,272✔
3339

3340
    std::string description(util::serializer::SerialisationState& state) const override
3341
    {
818✔
3342
        return m_list.description(state) + util::serializer::value_separator + "length";
818✔
3343
    }
818✔
3344

3345
    util::Optional<ExpressionComparisonType> get_comparison_type() const override
3346
    {
5,196✔
3347
        return m_list.get_comparison_type();
5,196✔
3348
    }
5,196✔
3349

3350
private:
3351
    ColumnsCollection<T> m_list;
3352
};
3353

3354

3355
template <typename T>
3356
SizeOperator<int64_t> ColumnsCollection<T>::size()
3357
{
3,540✔
3358
    std::unique_ptr<Subexpr> ptr(new ColumnListSize<T>(*this));
3,540✔
3359
    return SizeOperator<int64_t>(std::move(ptr));
3,540✔
3360
}
3,540✔
3361

3362
template <typename T, typename Operation>
3363
class CollectionColumnAggregate : public Subexpr2<decltype(Operation().result())> {
3364
public:
3365
    CollectionColumnAggregate(ColumnsCollection<T> column)
3366
        : m_columns_collection(std::move(column))
3367
    {
5,580✔
3368
        if (m_columns_collection.m_column_key.is_dictionary()) {
5,580✔
3369
            m_dictionary_key_type = m_columns_collection.m_link_map.get_target_table()->get_dictionary_key_type(
1,104✔
3370
                m_columns_collection.m_column_key);
1,104✔
3371
        }
1,104✔
3372
    }
5,580✔
3373

3374
    CollectionColumnAggregate(const CollectionColumnAggregate& other)
3375
        : m_columns_collection(other.m_columns_collection)
3376
        , m_dictionary_key_type(other.m_dictionary_key_type)
3377
    {
10,868✔
3378
    }
10,868✔
3379

3380
    DataType get_type() const override
3381
    {
5,952✔
3382
        return DataType(m_columns_collection.m_column_key.get_type());
5,952✔
3383
    }
5,952✔
3384

3385
    std::unique_ptr<Subexpr> clone() const override
3386
    {
10,868✔
3387
        return make_subexpr<CollectionColumnAggregate>(*this);
10,868✔
3388
    }
10,868✔
3389

3390
    ConstTableRef get_base_table() const override
3391
    {
5,580✔
3392
        return m_columns_collection.get_base_table();
5,580✔
3393
    }
5,580✔
3394

3395
    void set_base_table(ConstTableRef table) override
3396
    {
6,060✔
3397
        m_columns_collection.set_base_table(table);
6,060✔
3398
    }
6,060✔
3399

3400
    void set_cluster(const Cluster* cluster) override
3401
    {
10,396✔
3402
        m_columns_collection.set_cluster(cluster);
10,396✔
3403
    }
10,396✔
3404

3405
    void collect_dependencies(std::vector<TableKey>& tables) const override
3406
    {
488✔
3407
        m_columns_collection.collect_dependencies(tables);
488✔
3408
    }
488✔
3409

3410
    void evaluate(size_t index, ValueBase& destination) override
3411
    {
1,132,552✔
3412
        if (m_dictionary_key_type) {
1,132,552✔
3413
            if (m_columns_collection.links_exist()) {
17,508!
3414
                std::vector<ObjKey> links = m_columns_collection.m_link_map.get_links(index);
1,212✔
3415
                auto sz = links.size();
1,212✔
3416

606✔
3417
                destination.init_for_links(m_columns_collection.m_link_map.only_unary_links(), sz);
1,212✔
3418
                if (sz == 0 && m_columns_collection.m_link_map.only_unary_links()) {
1,212!
3419
                    set_value_for_empty_dictionary(destination, 0);
324✔
3420
                }
324✔
3421
                for (size_t t = 0; t < sz; t++) {
3,180!
3422
                    const Obj obj = m_columns_collection.m_link_map.get_target_table()->get_object(links[t]);
1,968✔
3423
                    auto dict = obj.get_dictionary(m_columns_collection.m_column_key);
1,968✔
3424
                    if (dict.size() > 0) {
1,968!
3425
                        destination.set(t, do_dictionary_agg(dict));
1,644✔
3426
                    }
1,644✔
3427
                    else {
324✔
3428
                        set_value_for_empty_dictionary(destination, t);
324✔
3429
                    }
324✔
3430
                }
1,968✔
3431
            }
1,212✔
3432
            else {
16,296✔
3433
                if (auto ref = m_columns_collection.m_leaf->get(index)) {
16,296!
3434
                    Allocator& alloc = m_columns_collection.get_base_table()->get_alloc();
15,732✔
3435
                    Dictionary dict(alloc, m_columns_collection.m_column_key, to_ref(ref));
15,732✔
3436
                    destination.set(0, do_dictionary_agg(dict));
15,732✔
3437
                }
15,732✔
3438
                else {
564✔
3439
                    set_value_for_empty_dictionary(destination, 0);
564✔
3440
                }
564✔
3441
            }
16,296✔
3442
        }
17,508✔
3443
        else {
1,115,044✔
3444
            Allocator& alloc = m_columns_collection.get_alloc();
1,115,044✔
3445
            Value<int64_t> list_refs;
1,115,044✔
3446
            m_columns_collection.get_lists(index, list_refs, 1);
1,115,044✔
3447
            size_t sz = list_refs.size();
1,115,044✔
3448
            REALM_ASSERT_DEBUG(sz > 0 || list_refs.m_from_list);
1,115,044!
3449
            // The result is an aggregate value for each table
557,522✔
3450
            destination.init_for_links(!list_refs.m_from_list, sz);
1,115,044✔
3451
            for (size_t i = 0; i < list_refs.size(); i++) {
2,230,432✔
3452
                auto list_ref = to_ref(list_refs[i].get_int());
1,115,388✔
3453
                Operation op;
1,115,388✔
3454
                if (list_ref) {
1,115,388✔
3455
                    if constexpr (realm::is_any_v<T, ObjectId, Int, Bool, UUID>) {
1,086,348✔
3456
                        if (m_columns_collection.m_is_nullable_storage) {
543,818✔
3457
                            accumulate<util::Optional<T>>(op, alloc, list_ref);
190,120✔
3458
                        }
190,120✔
3459
                        else {
33,500✔
3460
                            accumulate<T>(op, alloc, list_ref);
33,500✔
3461
                        }
33,500✔
3462
                    }
222,332✔
3463
                    else {
865,304✔
3464
                        accumulate<T>(op, alloc, list_ref);
865,304✔
3465
                    }
865,304✔
3466
                }
1,086,348✔
3467
                if (op.is_null()) {
1,115,388✔
3468
                    destination.set_null(i);
23,036✔
3469
                }
23,036✔
3470
                else {
1,092,352✔
3471
                    destination.set(i, op.result());
1,092,352✔
3472
                }
1,092,352✔
3473
            }
1,115,388✔
3474
        }
1,115,044✔
3475
    }
1,132,552✔
3476

3477
    std::string description(util::serializer::SerialisationState& state) const override
3478
    {
1,616✔
3479
        return m_columns_collection.description(state) + util::serializer::value_separator + Operation::description();
1,616✔
3480
    }
1,616✔
3481

3482
private:
3483
    template <typename StorageType>
3484
    void accumulate(Operation& op, Allocator& alloc, ref_type list_ref)
3485
    {
1,086,348✔
3486
        BPlusTree<StorageType> list(alloc);
1,086,348✔
3487
        list.init_from_ref(list_ref);
1,086,348✔
3488
        size_t s = list.size();
1,086,348✔
3489
        for (unsigned j = 0; j < s; j++) {
6,433,840✔
3490
            auto v = list.get(j);
5,347,492✔
3491
            if (!value_is_null(v)) {
5,347,492✔
3492
                if constexpr (std::is_same_v<StorageType, util::Optional<T>>) {
4,476,020✔
3493
                    op.accumulate(*v);
3,691,444✔
3494
                }
3,691,444✔
3495
                else {
3,691,444✔
3496
                    op.accumulate(v);
3,691,444✔
3497
                }
3,691,444✔
3498
            }
4,476,020✔
3499
        }
5,347,492✔
3500
    }
1,086,348✔
3501

3502
    Mixed do_dictionary_agg(const Dictionary& dict)
3503
    {
17,376✔
3504
        if constexpr (std::is_same_v<Operation, aggregate_operations::Maximum<Mixed>>) {
17,376✔
3505
            return *dict.do_max();
12,060✔
3506
        }
12,060✔
3507
        else if constexpr (std::is_same_v<Operation, aggregate_operations::Minimum<Mixed>>) {
12,060✔
3508
            return *dict.do_min();
8,064✔
3509
        }
8,064✔
3510
        else if constexpr (std::is_same_v<Operation, aggregate_operations::Average<Mixed>>) {
8,064✔
3511
            return *dict.do_avg();
4,068✔
3512
        }
4,068✔
3513
        else if constexpr (std::is_same_v<Operation, aggregate_operations::Sum<Mixed>>) {
4,068✔
3514
            return *dict.do_sum();
4,068✔
3515
        }
4,068✔
3516
        REALM_UNREACHABLE();
3517
    }
×
3518

3519
    inline void set_value_for_empty_dictionary(ValueBase& destination, size_t ndx)
3520
    {
1,212✔
3521
        if constexpr (std::is_same_v<Operation, aggregate_operations::Sum<Mixed>>) {
1,212✔
3522
            destination.set(ndx, 0); // the sum of nothing is zero
828✔
3523
        }
828✔
3524
        else {
828✔
3525
            destination.set_null(ndx);
828✔
3526
        }
828✔
3527
    }
1,212✔
3528

3529
    ColumnsCollection<T> m_columns_collection;
3530
    util::Optional<DataType> m_dictionary_key_type;
3531
};
3532

3533
template <class Operator>
3534
Query compare(const Subexpr2<Link>& left, const Obj& obj)
3535
{
280✔
3536
    static_assert(std::is_same_v<Operator, Equal> || std::is_same_v<Operator, NotEqual>,
280✔
3537
                  "Links can only be compared for equality.");
280✔
3538
    const Columns<Link>* column = dynamic_cast<const Columns<Link>*>(&left);
280✔
3539
    if (column) {
280✔
3540
        const LinkMap& link_map = column->link_map();
280✔
3541
        REALM_ASSERT(link_map.get_target_table()->get_key() == obj.get_table()->get_key());
280✔
3542
#ifdef REALM_OLDQUERY_FALLBACK
280✔
3543
        if (link_map.get_nb_hops() == 1) {
280✔
3544
            // We can fall back to Query::links_to for != and == operations on links
137✔
3545
            if (link_map.m_link_types[0] == col_type_Link || (link_map.m_link_types[0] == col_type_LinkList)) {
274✔
3546
                ConstTableRef t = column->get_base_table();
272✔
3547
                Query query(t);
272✔
3548

136✔
3549
                if (std::is_same_v<Operator, Equal>) {
272✔
3550
                    return query.equal(link_map.m_link_column_keys[0], obj.get_link());
166✔
3551
                }
166✔
3552
                else {
106✔
3553
                    return query.not_equal(link_map.m_link_column_keys[0], obj.get_link());
106✔
3554
                }
106✔
3555
            }
8✔
3556
        }
274✔
3557
#endif
280✔
3558
    }
280✔
3559
    return make_expression<Compare<Operator>>(left.clone(), make_subexpr<KeyValue>(obj.get_key()));
8✔
3560
}
8✔
3561
template <class Operator>
3562
Query compare(const Subexpr2<Link>& left, null)
3563
{
42✔
3564
    static_assert(std::is_same_v<Operator, Equal> || std::is_same_v<Operator, NotEqual>,
42✔
3565
                  "Links can only be compared for equality.");
42✔
3566
    return make_expression<Compare<Operator>>(left.clone(), make_subexpr<KeyValue>(ObjKey{}));
42✔
3567
}
42✔
3568

3569

3570
template <class T>
3571
class Columns : public ObjPropertyExpr<T> {
3572
    constexpr static bool requires_null_column = realm::is_any_v<T, int64_t, bool>;
3573

3574
public:
3575
    using LeafType = typename ColumnTypeTraits<T>::cluster_leaf_type;
3576
    using NullableLeafType =
3577
        std::conditional_t<requires_null_column, typename ColumnTypeTraits<util::Optional<T>>::cluster_leaf_type,
3578
                           LeafType>;
3579
    using ObjPropertyExpr<T>::links_exist;
3580
    using ObjPropertyBase::is_nullable;
3581

3582
    Columns(ColKey column, ConstTableRef table, std::vector<ColKey> links = {},
3583
            util::Optional<ExpressionComparisonType> type = util::none)
3584
        : ObjPropertyExpr<T>(column, table, std::move(links), type)
3585
    {
44,124✔
3586
    }
44,124✔
3587

3588
    Columns(const Columns& other)
3589
        : ObjPropertyExpr<T>(other)
3590
    {
25,428✔
3591
    }
25,428✔
3592

3593
    void set_cluster(const Cluster* cluster) override
3594
    {
27,104✔
3595
        if (links_exist()) {
27,104✔
3596
            m_link_map.set_cluster(cluster);
2,416✔
3597
            m_leaf = mpark::monostate();
2,416✔
3598
        }
2,416✔
3599
        else if (requires_null_column && is_nullable()) {
24,688!
3600
            auto& leaf = m_leaf.template emplace<NullableLeafType>(this->get_base_table()->get_alloc());
3,328✔
3601
            cluster->init_leaf(m_column_key, &leaf);
3,328✔
3602
        }
3,328✔
3603
        else {
21,360✔
3604
            auto& leaf = m_leaf.template emplace<LeafType>(this->get_base_table()->get_alloc());
21,360✔
3605
            cluster->init_leaf(m_column_key, &leaf);
21,360✔
3606
        }
21,360✔
3607
    }
27,104✔
3608

3609
    template <class LeafType2 = LeafType>
3610
    void evaluate_internal(size_t index, ValueBase& destination)
3611
    {
1,535,446✔
3612
        using U = typename LeafType2::value_type;
1,535,446✔
3613

774,212✔
3614
        if (links_exist()) {
1,535,446✔
3615
            REALM_ASSERT(mpark::holds_alternative<mpark::monostate>(m_leaf));
69,628✔
3616
            if (m_link_map.only_unary_links()) {
69,628✔
3617
                destination.init(false, 1);
20,136✔
3618
                destination.set_null(0);
20,136✔
3619
                if (auto link_translation_key = m_link_map.get_unary_link_or_not_found(index)) {
20,136✔
3620
                    const Obj obj = m_link_map.get_target_table()->get_object(link_translation_key);
19,688✔
3621
                    if (!obj.is_null(m_column_key))
19,688✔
3622
                        destination.set(0, obj.get<U>(m_column_key));
18,294✔
3623
                }
19,688✔
3624
            }
20,136✔
3625
            else {
49,492✔
3626
                // LinkList with more than 0 values. Create Value with payload for all fields
24,746✔
3627
                std::vector<ObjKey> links = m_link_map.get_links(index);
49,492✔
3628
                destination.init_for_links(m_link_map.only_unary_links(), links.size());
49,492✔
3629

24,746✔
3630
                for (size_t t = 0; t < links.size(); t++) {
148,574✔
3631
                    const Obj obj = m_link_map.get_target_table()->get_object(links[t]);
99,082✔
3632
                    if (obj.is_null(m_column_key))
99,082✔
3633
                        destination.set_null(t);
4✔
3634
                    else
99,078✔
3635
                        destination.set(t, obj.get<U>(m_column_key));
99,078✔
3636
                }
99,082✔
3637
            }
49,492✔
3638
        }
69,628✔
3639
        else {
1,465,818✔
3640
            auto leaf = mpark::get_if<LeafType2>(&m_leaf);
1,465,818✔
3641
            REALM_ASSERT(leaf);
1,465,818✔
3642
            // Not a Link column
739,398✔
3643
            size_t colsize = leaf->size();
1,465,818✔
3644

739,398✔
3645
            size_t rows = std::min(colsize - index, ValueBase::chunk_size);
1,465,818✔
3646

739,398✔
3647
            // Now load `ValueBase::chunk_size` rows from from the leaf into m_storage.
739,398✔
3648
            if constexpr (std::is_same_v<U, int64_t>) {
1,465,818✔
3649
                // If it's an integer leaf, then it contains the method get_chunk() which copies
623,792✔
3650
                // these values in a super fast way. If you want to modify 'chunk_size' then update Array::get_chunk()
623,792✔
3651
                REALM_ASSERT_3(ValueBase::chunk_size, ==, 8);
1,234,628✔
3652

623,792✔
3653
                int64_t res[ValueBase::chunk_size];
1,234,628✔
3654
                static_cast<const Array*>(leaf)->get_chunk(index, res);
1,234,628✔
3655
                destination.set(res, res + rows);
1,234,628✔
3656
                return;
1,234,628✔
3657
            }
1,234,628✔
3658

24✔
3659
            destination.init(false, rows);
26✔
3660
            for (size_t t = 0; t < rows; t++) {
1,882,638✔
3661
                if (leaf->is_null(index + t)) {
1,651,448✔
3662
                    destination.set_null(t);
778,246✔
3663
                }
778,246✔
3664
                else {
873,202✔
3665
                    destination.set(t, leaf->get(index + t));
873,202✔
3666
                }
873,202✔
3667
            }
1,651,448✔
3668
        }
26✔
3669
    }
1,535,446✔
3670

3671
    std::string description(util::serializer::SerialisationState& state) const override
3672
    {
2,120✔
3673
        return state.describe_expression_type(this->m_comparison_type) +
2,120✔
3674
               state.describe_columns(m_link_map, m_column_key);
2,120✔
3675
    }
2,120✔
3676

3677
    // Load values from Column into destination
3678
    void evaluate(size_t index, ValueBase& destination) override
3679
    {
1,535,430✔
3680
        if (is_nullable()) {
1,535,430✔
3681
            evaluate_internal<NullableLeafType>(index, destination);
198,420✔
3682
        }
198,420✔
3683
        else {
1,337,010✔
3684
            evaluate_internal<LeafType>(index, destination);
1,337,010✔
3685
        }
1,337,010✔
3686
    }
1,535,430✔
3687

3688
    void evaluate(ObjKey key, ValueBase& destination) override
3689
    {
38,040✔
3690
        destination.init(false, 1);
38,040✔
3691
        auto table = m_link_map.get_target_table();
38,040✔
3692
        auto obj = table.unchecked_ptr()->get_object(key);
38,040✔
3693
        if (requires_null_column && is_nullable()) {
38,040!
3694
            destination.set(0, obj.template get<util::Optional<T>>(m_column_key));
1,224✔
3695
        }
1,224✔
3696
        else {
36,816✔
3697
            destination.set(0, obj.template get<T>(m_column_key));
36,816✔
3698
        }
36,816✔
3699
    }
38,040✔
3700

3701
private:
3702
    using ObjPropertyExpr<T>::m_link_map;
3703
    using ObjPropertyExpr<T>::m_column_key;
3704
    using LeafStorage =
3705
        std::conditional_t<requires_null_column, mpark::variant<mpark::monostate, LeafType, NullableLeafType>,
3706
                           mpark::variant<mpark::monostate, LeafType>>;
3707
    LeafStorage m_leaf;
3708
};
3709

3710
template <typename T, typename Operation>
3711
class SubColumnAggregate;
3712

3713
// Defines a uniform interface for aggregation methods.
3714
class SubColumnBase {
3715
public:
3716
    virtual std::unique_ptr<Subexpr> max_of() = 0;
3717
    virtual std::unique_ptr<Subexpr> min_of() = 0;
3718
    virtual std::unique_ptr<Subexpr> sum_of() = 0;
3719
    virtual std::unique_ptr<Subexpr> avg_of() = 0;
3720
};
3721

3722
template <typename T>
3723
class SubColumns : public Subexpr, public SubColumnBase {
3724
public:
3725
    SubColumns(Columns<T>&& column, const LinkMap& link_map)
3726
        : m_column(std::move(column))
3727
        , m_link_map(link_map)
3728
    {
2,964✔
3729
    }
2,964✔
3730

3731
    DataType get_type() const final
3732
    {
×
3733
        return ColumnTypeTraits<T>::id;
×
3734
    }
×
3735

3736
    std::unique_ptr<Subexpr> clone() const override
3737
    {
688✔
3738
        return make_subexpr<SubColumns<T>>(*this);
688✔
3739
    }
688✔
3740

3741
    ConstTableRef get_base_table() const override
3742
    {
×
3743
        return m_link_map.get_base_table();
×
3744
    }
×
3745

3746
    void set_base_table(ConstTableRef table) override
3747
    {
×
3748
        m_link_map.set_base_table(table);
×
3749
        m_column.set_base_table(m_link_map.get_target_table());
×
3750
    }
×
3751

3752
    void collect_dependencies(std::vector<TableKey>& tables) const override
3753
    {
×
3754
        m_link_map.collect_dependencies(tables);
×
3755
    }
×
3756

3757
    void evaluate(size_t, ValueBase&) override
3758
    {
×
3759
        // SubColumns can only be used in an expression in conjunction with its aggregate methods.
3760
        REALM_ASSERT(false);
×
3761
    }
×
3762

3763
    std::string description(util::serializer::SerialisationState&) const override
3764
    {
×
3765
        return ""; // by itself there are no conditions, see SubColumnAggregate
×
3766
    }
×
3767

3768
    SubColumnAggregate<T, aggregate_operations::Minimum<T>> min() const
3769
    {
744✔
3770
        return {m_column, m_link_map};
744✔
3771
    }
744✔
3772

3773
    SubColumnAggregate<T, aggregate_operations::Maximum<T>> max() const
3774
    {
768✔
3775
        return {m_column, m_link_map};
768✔
3776
    }
768✔
3777

3778
    SubColumnAggregate<T, aggregate_operations::Sum<T>> sum() const
3779
    {
696✔
3780
        return {m_column, m_link_map};
696✔
3781
    }
696✔
3782

3783
    SubColumnAggregate<T, aggregate_operations::Average<T>> average() const
3784
    {
748✔
3785
        return {m_column, m_link_map};
748✔
3786
    }
748✔
3787

3788
    std::unique_ptr<Subexpr> max_of() override
3789
    {
200✔
3790
        if constexpr (realm::is_any_v<T, Int, Float, Double, Decimal128, Timestamp>) {
200✔
3791
            return max().clone();
200✔
3792
        }
200✔
3793
        else {
200✔
3794
            return {};
200✔
3795
        }
200✔
3796
    }
200✔
3797
    std::unique_ptr<Subexpr> min_of() override
3798
    {
192✔
3799
        if constexpr (realm::is_any_v<T, Int, Float, Double, Decimal128, Timestamp>) {
192✔
3800
            return min().clone();
192✔
3801
        }
192✔
3802
        else {
192✔
3803
            return {};
192✔
3804
        }
192✔
3805
    }
192✔
3806
    std::unique_ptr<Subexpr> sum_of() override
3807
    {
132✔
3808
        if constexpr (realm::is_any_v<T, Int, Float, Double, Decimal128>) {
132✔
3809
            return sum().clone();
4✔
3810
        }
4✔
3811
        else {
4✔
3812
            return {};
4✔
3813
        }
4✔
3814
    }
132✔
3815
    std::unique_ptr<Subexpr> avg_of() override
3816
    {
164✔
3817
        if constexpr (realm::is_any_v<T, Int, Float, Double, Decimal128>) {
164✔
3818
            return average().clone();
4✔
3819
        }
4✔
3820
        else {
4✔
3821
            return {};
4✔
3822
        }
4✔
3823
    }
164✔
3824

3825
private:
3826
    Columns<T> m_column;
3827
    LinkMap m_link_map;
3828
};
3829

3830
template <typename T, typename Operation>
3831
class SubColumnAggregate : public Subexpr2<decltype(Operation().result())> {
3832
public:
3833
    SubColumnAggregate(const Columns<T>& column, const LinkMap& link_map)
3834
        : m_column(column)
3835
        , m_link_map(link_map)
3836
    {
2,956✔
3837
    }
2,956✔
3838
    SubColumnAggregate(SubColumnAggregate const& other)
3839
        : m_column(other.m_column)
3840
        , m_link_map(other.m_link_map)
3841
    {
5,736✔
3842
    }
5,736✔
3843

3844
    std::unique_ptr<Subexpr> clone() const override
3845
    {
5,736✔
3846
        return make_subexpr<SubColumnAggregate>(*this);
5,736✔
3847
    }
5,736✔
3848

3849
    ConstTableRef get_base_table() const override
3850
    {
2,956✔
3851
        return m_link_map.get_base_table();
2,956✔
3852
    }
2,956✔
3853

3854
    void set_base_table(ConstTableRef table) override
3855
    {
3,436✔
3856
        m_link_map.set_base_table(table);
3,436✔
3857
        m_column.set_base_table(m_link_map.get_target_table());
3,436✔
3858
    }
3,436✔
3859

3860
    void set_cluster(const Cluster* cluster) override
3861
    {
4,348✔
3862
        m_link_map.set_cluster(cluster);
4,348✔
3863
    }
4,348✔
3864

3865
    void collect_dependencies(std::vector<TableKey>& tables) const override
3866
    {
436✔
3867
        m_link_map.collect_dependencies(tables);
436✔
3868
    }
436✔
3869

3870
    void evaluate(size_t index, ValueBase& destination) override
3871
    {
59,076✔
3872
        std::vector<ObjKey> keys = m_link_map.get_links(index);
59,076✔
3873
        std::sort(keys.begin(), keys.end());
59,076✔
3874

29,538✔
3875
        Operation op;
59,076✔
3876
        for (auto key : keys) {
49,718✔
3877
            Value<T> value;
40,360✔
3878
            m_column.evaluate(key, value);
40,360✔
3879
            size_t value_index = 0;
40,360✔
3880
            if (!value[value_index].is_null()) {
40,360✔
3881
                op.accumulate(value[value_index].template get<T>());
39,964✔
3882
            }
39,964✔
3883
        }
40,360✔
3884
        if (op.is_null()) {
59,076✔
3885
            destination.set_null(0);
20,972✔
3886
        }
20,972✔
3887
        else {
38,104✔
3888
            destination.set(0, op.result());
38,104✔
3889
        }
38,104✔
3890
    }
59,076✔
3891

3892
    std::string description(util::serializer::SerialisationState& state) const override
3893
    {
340✔
3894
        util::serializer::SerialisationState empty_state(state.group);
340✔
3895
        return state.describe_columns(m_link_map, ColKey()) + util::serializer::value_separator +
340✔
3896
               Operation::description() + util::serializer::value_separator + m_column.description(empty_state);
340✔
3897
    }
340✔
3898

3899
private:
3900
    Columns<T> m_column;
3901
    LinkMap m_link_map;
3902
};
3903

3904
class SubQueryCount : public Subexpr2<Int> {
3905
public:
3906
    SubQueryCount(const Query& q, const LinkMap& link_map)
3907
        : m_query(q)
3908
        , m_link_map(link_map)
3909
    {
412✔
3910
        REALM_ASSERT(q.produces_results_in_table_order());
412✔
3911
        REALM_ASSERT(m_query.get_table() == m_link_map.get_target_table());
412✔
3912
    }
412✔
3913

3914
    ConstTableRef get_base_table() const override
3915
    {
412✔
3916
        return m_link_map.get_base_table();
412✔
3917
    }
412✔
3918

3919
    void set_base_table(ConstTableRef table) override
3920
    {
440✔
3921
        m_link_map.set_base_table(table);
440✔
3922
        m_query.set_table(m_link_map.get_target_table().cast_away_const());
440✔
3923
    }
440✔
3924

3925
    void set_cluster(const Cluster* cluster) override
3926
    {
588✔
3927
        m_link_map.set_cluster(cluster);
588✔
3928
    }
588✔
3929

3930
    void collect_dependencies(std::vector<TableKey>& tables) const override
3931
    {
32✔
3932
        m_link_map.collect_dependencies(tables);
32✔
3933
    }
32✔
3934

3935
    void evaluate(size_t index, ValueBase& destination) override
3936
    {
3,928✔
3937
        std::vector<ObjKey> links = m_link_map.get_links(index);
3,928✔
3938
        // std::sort(links.begin(), links.end());
1,964✔
3939
        if (!m_initialized) {
3,928✔
3940
            m_query.init();
452✔
3941
            m_initialized = true;
452✔
3942
        }
452✔
3943

1,964✔
3944
        size_t count = std::accumulate(links.begin(), links.end(), size_t(0), [this](size_t running_count, ObjKey k) {
6,988✔
3945
            const Obj obj = m_link_map.get_target_table()->get_object(k);
6,988✔
3946
            return running_count + m_query.eval_object(obj);
6,988✔
3947
        });
6,988✔
3948

1,964✔
3949
        destination = Value<int64_t>(count);
3,928✔
3950
    }
3,928✔
3951

3952
    std::string description(util::serializer::SerialisationState& state) const override
3953
    {
144✔
3954
        REALM_ASSERT(m_link_map.get_base_table() != nullptr);
144✔
3955
        std::string target = state.describe_columns(m_link_map, ColKey());
144✔
3956
        std::string var_name = state.get_variable_name(m_link_map.get_base_table());
144✔
3957
        state.subquery_prefix_list.push_back(var_name);
144✔
3958
        std::string desc = "SUBQUERY(" + target + ", " + var_name + ", " + m_query.get_description(state) + ")" +
144✔
3959
                           util::serializer::value_separator + "@count";
144✔
3960
        state.subquery_prefix_list.pop_back();
144✔
3961
        return desc;
144✔
3962
    }
144✔
3963

3964
    std::unique_ptr<Subexpr> clone() const override
3965
    {
560✔
3966
        return make_subexpr<SubQueryCount>(*this);
560✔
3967
    }
560✔
3968

3969
    SubQueryCount(const SubQueryCount& other)
3970
        : m_query(other.m_query)
3971
        , m_link_map(other.m_link_map)
3972
        , m_initialized(false)
3973
    {
560✔
3974
    }
560✔
3975

3976
private:
3977
    Query m_query;
3978
    LinkMap m_link_map;
3979
    bool m_initialized = false;
3980
};
3981

3982
// The unused template parameter is a hack to avoid a circular dependency between table.hpp and query_expression.hpp.
3983
template <class>
3984
class SubQuery {
3985
public:
3986
    SubQuery(Columns<Link> link_column, Query query)
3987
        : m_query(std::move(query))
3988
        , m_link_map(link_column.link_map())
3989
    {
66✔
3990
        REALM_ASSERT(m_link_map.get_target_table() == m_query.get_table());
66✔
3991
    }
66✔
3992

3993
    SubQueryCount count() const
3994
    {
66✔
3995
        return SubQueryCount(m_query, m_link_map);
66✔
3996
    }
66✔
3997

3998
private:
3999
    Query m_query;
4000
    LinkMap m_link_map;
4001
};
4002

4003
template <class oper>
4004
class Operator : public Subexpr2<Mixed> {
4005
public:
4006
    Operator(std::unique_ptr<Subexpr> left, std::unique_ptr<Subexpr> right)
4007
        : m_left(std::move(left))
4008
        , m_right(std::move(right))
4009
    {
992✔
4010
        m_left_is_const = m_left->has_single_value();
992✔
4011
        m_right_is_const = m_right->has_single_value();
992✔
4012
        if (m_left_is_const) {
992✔
4013
            m_const_value = m_left->get_mixed();
152✔
4014
        }
152✔
4015
        else if (m_right_is_const) {
840✔
4016
            m_const_value = m_right->get_mixed();
412✔
4017
        }
412✔
4018
    }
992✔
4019

4020
    Operator(const Operator& other)
4021
        : m_left(other.m_left->clone())
4022
        , m_right(other.m_right->clone())
4023
        , m_left_is_const(other.m_left_is_const)
4024
        , m_right_is_const(other.m_right_is_const)
4025
        , m_const_value(other.m_const_value)
4026
    {
1,388✔
4027
    }
1,388✔
4028

4029
    Operator& operator=(const Operator& other)
4030
    {
4031
        if (this != &other) {
4032
            m_left = other.m_left->clone();
4033
            m_right = other.m_right->clone();
4034
        }
4035
        return *this;
4036
    }
4037

4038
    Operator(Operator&&) noexcept = delete;
4039
    Operator& operator=(Operator&&) noexcept = delete;
4040

4041
    DataType get_type() const override
4042
    {
2,920✔
4043
        return m_left->get_type();
2,920✔
4044
    }
2,920✔
4045

4046
    // See comment in base class
4047
    void set_base_table(ConstTableRef table) override
4048
    {
992✔
4049
        m_left->set_base_table(table);
992✔
4050
        m_right->set_base_table(table);
992✔
4051
    }
992✔
4052

4053
    void set_cluster(const Cluster* cluster) override
4054
    {
16,528✔
4055
        m_left->set_cluster(cluster);
16,528✔
4056
        m_right->set_cluster(cluster);
16,528✔
4057
    }
16,528✔
4058

4059
    // Recursively fetch tables of columns in expression tree. Used when user first builds a stand-alone expression
4060
    // and
4061
    // binds it to a Query at a later time
4062
    ConstTableRef get_base_table() const override
4063
    {
992✔
4064
        ConstTableRef l = m_left->get_base_table();
992✔
4065
        ConstTableRef r = m_right->get_base_table();
992✔
4066

496✔
4067
        // Queries do not support multiple different tables; all tables must be the same.
496✔
4068
        REALM_ASSERT(l == nullptr || r == nullptr || l == r);
992✔
4069

496✔
4070
        // nullptr pointer means expression which isn't yet associated with any table, or is a Value<T>
496✔
4071
        return bool(l) ? l : r;
902✔
4072
    }
992✔
4073

4074
    // destination = operator(left, right)
4075
    void evaluate(size_t index, ValueBase& destination) override
4076
    {
1,325,522✔
4077
        Value<T> result;
1,325,522✔
4078
        Value<T> left;
1,325,522✔
4079
        Value<T> right;
1,325,522✔
4080
        if (m_left_is_const) {
1,325,522✔
4081
            m_right->evaluate(index, right);
488✔
4082
            result.template fun_const<oper>(m_const_value, right);
488✔
4083
        }
488✔
4084
        else if (m_right_is_const) {
1,325,034✔
4085
            m_left->evaluate(index, left);
428,376✔
4086
            result.template fun_const<oper>(left, m_const_value);
428,376✔
4087
        }
428,376✔
4088
        else {
896,658✔
4089
            m_left->evaluate(index, left);
896,658✔
4090
            m_right->evaluate(index, right);
896,658✔
4091
            result.template fun<oper>(left, right);
896,658✔
4092
        }
896,658✔
4093
        destination = result;
1,325,522✔
4094
    }
1,325,522✔
4095

4096
    std::string description(util::serializer::SerialisationState& state) const override
4097
    {
204✔
4098
        std::string s = "(";
204✔
4099
        if (m_left) {
204✔
4100
            s += m_left->description(state);
204✔
4101
        }
204✔
4102
        s += (" " + oper::description() + " ");
204✔
4103
        if (m_right) {
204✔
4104
            s += m_right->description(state);
204✔
4105
        }
204✔
4106
        s += ")";
204✔
4107
        return s;
204✔
4108
    }
204✔
4109

4110
    util::Optional<ExpressionComparisonType> get_comparison_type() const override
4111
    {
1,171,820✔
4112
        if (!m_left_is_const) {
1,171,820✔
4113
            return m_left->get_comparison_type();
1,171,516✔
4114
        }
1,171,516✔
4115
        if (!m_right_is_const) {
304✔
4116
            return m_right->get_comparison_type();
304✔
4117
        }
304✔
UNCOV
4118
        return util::none;
×
UNCOV
4119
    }
×
4120

4121
    std::unique_ptr<Subexpr> clone() const override
4122
    {
1,388✔
4123
        return make_subexpr<Operator>(*this);
1,388✔
4124
    }
1,388✔
4125

4126
private:
4127
    std::unique_ptr<Subexpr> m_left;
4128
    std::unique_ptr<Subexpr> m_right;
4129
    bool m_left_is_const;
4130
    bool m_right_is_const;
4131
    Mixed m_const_value;
4132
};
4133

4134
class CompareBase : public Expression {
4135
public:
4136
    CompareBase(std::unique_ptr<Subexpr> left, std::unique_ptr<Subexpr> right)
4137
        : m_left(std::move(left))
4138
        , m_right(std::move(right))
4139
    {
62,160✔
4140
        if (m_left->has_constant_evaluation()) {
62,160✔
4141
            m_left_const_values = dynamic_cast<ValueBase*>(m_left.get());
27,024✔
4142
        }
27,024✔
4143
        if (m_right->has_constant_evaluation()) {
62,160✔
4144
            m_right_const_values = dynamic_cast<ValueBase*>(m_right.get());
27,234✔
4145
        }
27,234✔
4146
        REALM_ASSERT(!(m_left_const_values && m_right_const_values));
62,160✔
4147
    }
62,160✔
4148

4149
    // See comment in base class
4150
    void set_base_table(ConstTableRef table) override
4151
    {
67,410✔
4152
        m_left->set_base_table(table);
67,410✔
4153
        m_right->set_base_table(table);
67,410✔
4154
    }
67,410✔
4155

4156
    void set_cluster(const Cluster* cluster) override
4157
    {
90,234✔
4158
        if (m_has_matches) {
90,234✔
4159
            m_cluster = cluster;
1,158✔
4160
        }
1,158✔
4161
        else {
89,076✔
4162
            m_left->set_cluster(cluster);
89,076✔
4163
            m_right->set_cluster(cluster);
89,076✔
4164
        }
89,076✔
4165
    }
90,234✔
4166

4167
    // Recursively fetch tables of columns in expression tree. Used when user first builds a stand-alone expression
4168
    // and binds it to a Query at a later time
4169
    ConstTableRef get_base_table() const override
4170
    {
62,160✔
4171
        ConstTableRef l = m_left->get_base_table();
62,160✔
4172
        ConstTableRef r = m_right->get_base_table();
62,160✔
4173

31,080✔
4174
        // All main tables in each subexpression of a query (table.columns() or table.link()) must be the same.
31,080✔
4175
        REALM_ASSERT(l == nullptr || r == nullptr || l == r);
62,160✔
4176

31,080✔
4177
        // nullptr pointer means expression which isn't yet associated with any table, or is a Value<T>
31,080✔
4178
        return (l) ? l : r;
48,627✔
4179
    }
62,160✔
4180

4181
    void collect_dependencies(std::vector<TableKey>& tables) const override
4182
    {
3,150✔
4183
        m_left->collect_dependencies(tables);
3,150✔
4184
        m_right->collect_dependencies(tables);
3,150✔
4185
    }
3,150✔
4186

4187
    size_t find_first_with_matches(size_t start, size_t end) const
4188
    {
15,630✔
4189
        if (m_index_end == 0 || start >= end)
15,630✔
4190
            return not_found;
30✔
4191

7,821✔
4192
        ObjKey first_key = m_cluster->get_real_key(start);
15,600✔
4193
        ObjKey actual_key;
15,600✔
4194

7,821✔
4195
        // Sequential lookup optimization: when the query isn't constrained
7,821✔
4196
        // to a LnkLst we'll get find_first() requests in ascending order,
7,821✔
4197
        // so we can do a simple linear scan.
7,821✔
4198
        if (m_index_get < m_index_end && m_matches[m_index_get] <= first_key) {
15,600✔
4199
            actual_key = m_matches[m_index_get];
14,802✔
4200
            // skip through keys which are in "earlier" leafs than the one selected by start..end:
7,419✔
4201
            while (first_key > actual_key) {
28,587✔
4202
                m_index_get++;
14,535✔
4203
                if (m_index_get == m_index_end)
14,535✔
4204
                    return not_found;
750✔
4205
                actual_key = m_matches[m_index_get];
13,785✔
4206
            }
13,785✔
4207
        }
14,802✔
4208
        // Otherwise if we get requests out of order we have to do a more
402✔
4209
        // expensive binary search
402✔
4210
        else {
798✔
4211
            auto it = std::lower_bound(m_matches.begin(), m_matches.end(), first_key);
798✔
4212
            if (it == m_matches.end())
798✔
4213
                return not_found;
×
4214
            actual_key = *it;
798✔
4215
        }
798✔
4216

7,821✔
4217
        // if actual key is bigger than last key, it is not in this leaf
7,821✔
4218
        ObjKey last_key = start + 1 == end ? first_key : m_cluster->get_real_key(end - 1);
15,225✔
4219
        if (actual_key > last_key)
14,850✔
4220
            return not_found;
75✔
4221

7,407✔
4222
        // key is known to be in this leaf, so find key whithin leaf keys
7,407✔
4223
        return m_cluster->lower_bound_key(ObjKey(actual_key.value - m_cluster->get_offset()));
14,775✔
4224
    }
14,775✔
4225

4226
protected:
4227
    CompareBase(const CompareBase& other)
4228
        : m_left(other.m_left->clone())
4229
        , m_right(other.m_right->clone())
4230
    {
56,994✔
4231
        if (m_left->has_constant_evaluation()) {
56,994✔
4232
            m_left_const_values = dynamic_cast<ValueBase*>(m_left.get());
20,364✔
4233
        }
20,364✔
4234
        if (m_right->has_constant_evaluation()) {
56,994✔
4235
            m_right_const_values = dynamic_cast<ValueBase*>(m_right.get());
28,818✔
4236
        }
28,818✔
4237
    }
56,994✔
4238

4239
    std::unique_ptr<Subexpr> m_left;
4240
    std::unique_ptr<Subexpr> m_right;
4241
    const Cluster* m_cluster;
4242
    ValueBase* m_left_const_values = nullptr;
4243
    ValueBase* m_right_const_values = nullptr;
4244
    bool m_has_matches = false;
4245
    std::vector<ObjKey> m_matches;
4246
    mutable size_t m_index_get = 0;
4247
    size_t m_index_end = 0;
4248
};
4249

4250
template <class TCond>
4251
class Compare : public CompareBase {
4252
public:
4253
    using CompareBase::CompareBase;
4254

4255
    double init() override
4256
    {
66,770✔
4257
        double dT = 50.0;
66,770✔
4258
        if ((m_left->has_single_value()) || (m_right->has_single_value())) {
66,770✔
4259
            dT = 10.0;
55,662✔
4260
            if constexpr (std::is_same_v<TCond, Equal>) {
55,662✔
4261
                // If the property not being constant has a search index we can speed things up by
16,659✔
4262
                // finding all matches up front.
16,659✔
4263
                Mixed const_value;
33,318✔
4264
                Subexpr* column;
33,318✔
4265
                if (m_left->has_single_value()) {
33,318✔
4266
                    const_value = m_left->get_mixed();
14,610✔
4267
                    column = m_right.get();
14,610✔
4268
                }
14,610✔
4269
                else {
18,708✔
4270
                    const_value = m_right->get_mixed();
18,708✔
4271
                    column = m_left.get();
18,708✔
4272
                }
18,708✔
4273

16,659✔
4274
                if (column->has_search_index() &&
33,318✔
4275
                    column->get_comparison_type().value_or(ExpressionComparisonType::Any) ==
17,337✔
4276
                        ExpressionComparisonType::Any) {
1,332✔
4277
                    if (const_value.is_null()) {
1,308✔
4278
                        const ObjPropertyBase* prop = dynamic_cast<const ObjPropertyBase*>(m_right.get());
348✔
4279
                        // when checking for null across links, null links are considered matches,
174✔
4280
                        // so we must compute the slow matching even if there is an index.
174✔
4281
                        if (!prop || prop->links_exist()) {
348✔
4282
                            return dT;
282✔
4283
                        }
282✔
4284
                        else {
66✔
4285
                            m_matches = column->find_all(Mixed());
66✔
4286
                        }
66✔
4287
                    }
348✔
4288
                    else {
960✔
4289
                        if (column->get_type() != const_value.get_type()) {
960✔
4290
                            // If the type we are looking for is not the same type as the target
3✔
4291
                            // column, we cannot use the index
3✔
4292
                            return dT;
6✔
4293
                        }
6✔
4294
                        m_matches = column->find_all(const_value);
954✔
4295
                    }
954✔
4296
                    // Sort
654✔
4297
                    std::sort(m_matches.begin(), m_matches.end());
1,164✔
4298
                    // Remove all duplicates
510✔
4299
                    m_matches.erase(std::unique(m_matches.begin(), m_matches.end()), m_matches.end());
1,020✔
4300

510✔
4301
                    m_has_matches = true;
1,020✔
4302
                    m_index_get = 0;
1,020✔
4303
                    m_index_end = m_matches.size();
1,020✔
4304
                    dT = 0;
1,020✔
4305
                }
1,020✔
4306
            }
33,318✔
4307
        }
55,662✔
4308

33,385✔
4309
        return dT;
52,312✔
4310
    }
66,770✔
4311

4312
    size_t find_first(size_t start, size_t end) const override
4313
    {
1,654,564✔
4314
        if (m_has_matches) {
1,654,564✔
4315
            return find_first_with_matches(start, end);
15,630✔
4316
        }
15,630✔
4317

818,213✔
4318
        size_t match;
1,638,934✔
4319
        ValueBase left_buf;
1,638,934✔
4320
        ValueBase right_buf;
1,638,934✔
4321
        const util::Optional<ExpressionComparisonType> left_cmp_type = m_left->get_comparison_type();
1,638,934✔
4322
        const util::Optional<ExpressionComparisonType> right_cmp_type = m_right->get_comparison_type();
1,638,934✔
4323

818,213✔
4324
        ValueBase* left = m_left_const_values ? m_left_const_values : &left_buf;
1,438,626✔
4325
        ValueBase* right = m_right_const_values ? m_right_const_values : &right_buf;
1,555,681✔
4326

818,213✔
4327
        for (; start < end;) {
3,487,108✔
4328
            if (!m_left_const_values)
3,427,652✔
4329
                m_left->evaluate(start, left_buf);
943,103✔
4330
            if (!m_right_const_values)
3,427,652✔
4331
                m_right->evaluate(start, right_buf);
2,765,728✔
4332
            match = ValueBase::template compare<TCond>(*left, *right, left_cmp_type, right_cmp_type);
3,427,652✔
4333
            if (match != not_found && match + start < end)
3,427,652✔
4334
                return start + match;
1,579,478✔
4335

926,048✔
4336
            size_t rows = (left->m_from_list || right->m_from_list) ? 1 : std::min(right->size(), left->size());
1,848,174✔
4337
            start += rows;
1,848,174✔
4338
        }
1,848,174✔
4339

818,213✔
4340
        return not_found; // no match
847,841✔
4341
    }
1,638,934✔
4342

4343
    std::string description(util::serializer::SerialisationState& state) const override
4344
    {
15,996✔
4345
        if constexpr (realm::is_any_v<TCond, BeginsWith, BeginsWithIns, EndsWith, EndsWithIns, Contains, ContainsIns,
15,996✔
4346
                                      Like, LikeIns>) {
7,998✔
4347
            // these string conditions have the arguments reversed but the order is important
7,380✔
4348
            // operations ==, and != can be reversed because the produce the same results both ways
7,380✔
4349
            return util::serializer::print_value(util::format("%1 %2 %3", m_right->description(state),
7,998✔
4350
                                                              TCond::description(), m_left->description(state)));
7,998✔
4351
        }
7,998✔
4352
        else {
14,760✔
4353
            state.target_table = m_right->get_target_table();
14,760✔
4354
            std::string ret = m_left->description(state) + " " + TCond::description() + " ";
14,760✔
4355
            state.target_table = m_left->get_target_table();
14,760✔
4356
            ret += m_right->description(state);
14,760✔
4357
            return ret;
14,760✔
4358
        }
14,760✔
4359
    }
15,996✔
4360

4361
    std::unique_ptr<Expression> clone() const override
4362
    {
53,248✔
4363
        return std::unique_ptr<Expression>(new Compare(*this));
53,248✔
4364
    }
53,248✔
4365
};
4366
} // namespace realm
4367
#endif // REALM_QUERY_EXPRESSION_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

© 2025 Coveralls, Inc