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

realm / realm-core / github_pull_request_281922

31 Oct 2023 09:13AM UTC coverage: 90.445% (-0.08%) from 90.528%
github_pull_request_281922

Pull #7039

Evergreen

jedelbo
Merge branch 'next-major' into je/global-key
Pull Request #7039: Remove ability to synchronize objects without primary key

95324 of 175822 branches covered (0.0%)

101 of 105 new or added lines in 13 files covered. (96.19%)

238 existing lines in 19 files now uncovered.

232657 of 257235 relevant lines covered (90.45%)

6351359.67 hits per line

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

93.24
/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
64,012✔
166

167
namespace realm {
168

169
template <class T>
170
T minimum(T a, T b)
171
{
2,901,948✔
172
    return a < b ? a : b;
2,736,786✔
173
}
2,901,948✔
174

175
struct Plus {
176
    Mixed operator()(Mixed v1, Mixed v2) const
177
    {
3,343,780✔
178
        return v1 + v2;
3,343,780✔
179
    }
3,343,780✔
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
    {
1,677,744✔
189
        return v1 - v2;
1,677,744✔
190
    }
1,677,744✔
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
    {
672,204✔
200
        return v1 / v2;
672,204✔
201
    }
672,204✔
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
    {
673,008✔
211
        return v1 * v2;
673,008✔
212
    }
673,008✔
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;
9,202,965✔
249
    ValueBase(const ValueType& init_val)
250
    {
747,438✔
251
        m_first[0] = init_val;
747,438✔
252
    }
747,438✔
253
    ~ValueBase()
254
    {
9,999,837✔
255
        dealloc();
9,999,837✔
256
    }
9,999,837✔
257
    ValueBase(const ValueBase& other)
258
    {
46,782✔
259
        *this = other;
46,782✔
260
    }
46,782✔
261

262
    ValueBase& operator=(const ValueBase& other)
263
    {
1,288,368✔
264
        init(other.m_from_list, other.size());
1,288,368✔
265
        set(other.begin(), other.end());
1,288,368✔
266
        return *this;
1,288,368✔
267
    }
1,288,368✔
268

269
    size_t size() const
270
    {
19,328,634✔
271
        return m_size;
19,328,634✔
272
    }
19,328,634✔
273

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

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

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

298
    template <class T>
299
    void set(size_t ndx, const T& val)
300
    {
33,651,145✔
301
        if constexpr (std::is_same<T, float>::value || std::is_same<T, double>::value) {
33,651,145✔
302
            m_first[ndx] = null::is_null_float(val) ? ValueType() : ValueType(val);
22,327,967✔
303
        }
11,830,343✔
304
        else {
32,399,535✔
305
            m_first[ndx] = ValueType(val);
32,399,535✔
306
        }
32,399,535✔
307
    }
33,651,145✔
308

309
    template <class T>
310
    void set(T b, T e)
311
    {
2,213,006✔
312
        size_t i = 0;
2,213,006✔
313
        for (auto from = b; from != e; ++from) {
18,132,342✔
314
            set(i, *from);
15,919,336✔
315
            i++;
15,919,336✔
316
        }
15,919,336✔
317
    }
2,213,006✔
318

319
    ValueType& operator[](size_t n)
320
    {
9,295,680✔
321
        return m_first[n];
9,295,680✔
322
    }
9,295,680✔
323

324
    const ValueType& operator[](size_t n) const
325
    {
10,716,904✔
326
        return m_first[n];
10,716,904✔
327
    }
10,716,904✔
328

329
    const ValueType& get(size_t n) const
330
    {
755,157✔
331
        return m_first[n];
755,157✔
332
    }
755,157✔
333

334
    ValueType* begin()
335
    {
183,855✔
336
        return m_first;
183,855✔
337
    }
183,855✔
338
    const ValueType* begin() const
339
    {
1,288,773✔
340
        return m_first;
1,288,773✔
341
    }
1,288,773✔
342

343
    ValueType* end()
344
    {
145,152✔
345
        return m_first + m_size;
145,152✔
346
    }
145,152✔
347
    const ValueType* end() const
348
    {
1,288,776✔
349
        return m_first + m_size;
1,288,776✔
350
    }
1,288,776✔
351
    void sort()
352
    {
1,092✔
353
        if (!m_sorted) {
1,092✔
354
            std::sort(begin(), end());
972✔
355
            m_sorted = true;
972✔
356
        }
972✔
357
    }
1,092✔
358
    template <class TOperator>
359
    REALM_FORCEINLINE void fun_const(const ValueType& const_value, const ValueBase& right)
360
    {
488✔
361
        TOperator o;
488✔
362
        // Operate on values one-by-one
244✔
363
        size_t sz = right.size();
488✔
364
        init(right.m_from_list, sz);
488✔
365
        for (size_t i = 0; i < sz; i++) {
1,560✔
366
            set(i, o(const_value, right[i]));
1,072✔
367
        }
1,072✔
368
    }
488✔
369
    template <class TOperator>
370
    REALM_FORCEINLINE void fun_const(const ValueBase& left, const ValueType& const_value)
371
    {
255,540✔
372
        TOperator o;
255,540✔
373
        // Operate on values one-by-one
130,938✔
374
        size_t sz = left.size();
255,540✔
375
        init(left.m_from_list, sz);
255,540✔
376
        for (size_t i = 0; i < sz; i++) {
2,271,036✔
377
            set(i, o(left[i], const_value));
2,015,496✔
378
        }
2,015,496✔
379
    }
255,540✔
380
    template <class TOperator>
381
    REALM_FORCEINLINE void fun(const ValueBase& left, const ValueBase& right)
382
    {
550,988✔
383
        TOperator o;
550,988✔
384

281,830✔
385
        if (!left.m_from_list && !right.m_from_list) {
550,988✔
386
            // Operate on values one-by-one (one value is one row; no links)
281,828✔
387
            size_t min = std::min(left.size(), right.size());
550,984✔
388
            init(false, min);
550,984✔
389

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

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

414
            auto right_value = right[0];
×
415
            for (size_t i = 0; i < left.size(); i++) {
×
416
                set(i, o(left[i], right_value));
×
417
            }
×
418
        }
×
419
    }
550,988✔
420

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

1,514,561✔
430
        if (!left.m_from_list && !right.m_from_list) {
3,027,295✔
431
            // ALL/NONE not supported for non list types
1,384,148✔
432
            REALM_ASSERT_DEBUG(!left_cmp_type || *left_cmp_type == Compare::Any);
2,766,370!
433
            REALM_ASSERT_DEBUG(!right_cmp_type || *right_cmp_type == Compare::Any);
2,766,370!
434
            // Compare values one-by-one (one value is one row; no link lists)
1,384,148✔
435
            size_t min = minimum(left.size(), right.size());
2,766,370✔
436
            for (size_t m = 0; m < min; m++) {
4,338,693✔
437
                if (c(left[m], right[m]))
2,967,628✔
438
                    return m;
1,395,305✔
439
            }
2,967,628✔
440
            return not_found;
2,069,739✔
441
        }
260,925✔
442

130,413✔
443
        if (left.m_from_list && right.m_from_list && !left_cmp_type && !right_cmp_type) {
260,925!
444
            // Both lists and no ANY, NONE, ALL specified - simple element by element comparison
2,150✔
445
            if (left.size() != right.size()) {
4,300!
446
                if constexpr (std::is_same_v<TCond, NotEqual>) {
2,664✔
447
                    return 0; // mismatch size
2,412✔
448
                }
2,412✔
449
                else {
2,412✔
450
                    return not_found;
2,412✔
451
                }
2,412✔
452
            }
1,528✔
453
            for (size_t i = 0; i < left.size(); ++i) {
3,636!
454
                if (!c(left[i], right[i])) {
2,876!
455
                    return not_found;
876✔
456
                }
876✔
457
            }
2,876✔
458
            return 0; // all elements matched in the right order
1,198✔
459
        }
256,625✔
460

128,263✔
461
        // if one side omitted a comparison type, assume ANY
128,263✔
462
        const Compare compare_left = left_cmp_type.value_or(Compare::Any);
256,625✔
463
        const Compare compare_right = right_cmp_type.value_or(Compare::Any);
256,625✔
464

128,263✔
465
        size_t left_size = left.m_from_list ? left.size() : 1;
196,085✔
466
        size_t right_size = right.m_from_list ? right.size() : 1;
196,753✔
467

128,263✔
468
        if (left_size > 2 && right_size > 2) {
256,625!
469
            left.sort();
476✔
470
            right.sort();
476✔
471

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

93,858✔
517
        if constexpr (realm::is_any_v<TCond, BeginsWith, BeginsWithIns, EndsWith, EndsWithIns, Contains, ContainsIns,
256,499✔
518
                                      Like, LikeIns>) {
128,200✔
519
            // The string operators have the arguments reversed so we have to iterate right in the
119,953✔
520
            // outer loop as this is actually the left argument
119,953✔
521
            auto left_matches = [&](const QueryValue& right_val) {
132,238✔
522
                for (size_t i = 0; i < left_size; i++) {
45,102✔
523
                    if (c(left[i], right_val)) {
25,530✔
524
                        // match
2,391✔
525
                        if (compare_left == Compare::Any) {
4,782✔
526
                            return true;
4,554✔
527
                        }
4,554✔
528
                        if (compare_left == Compare::None) {
228!
529
                            return false; // one matched
60✔
530
                        }
60✔
531
                    }
20,748✔
532
                    else {
20,748✔
533
                        // no match
10,374✔
534
                        if (compare_left == Compare::All) {
20,748✔
535
                            return false;
384✔
536
                        }
384✔
537
                    }
20,748✔
538
                }
25,530✔
539
                return compare_left == Compare::None || compare_left == Compare::All;
22,071✔
540
            };
24,570✔
541

119,953✔
542
            for (size_t i = 0; i < right_size; i++) {
137,902✔
543
                if (left_matches(right[i])) {
24,570✔
544
                    if (compare_right == Compare::Any) {
4,734✔
545
                        return 0;
2,790✔
546
                    }
2,790✔
547
                    if (compare_right == Compare::None) {
1,944✔
548
                        return not_found; // one matched
1,068✔
549
                    }
1,068✔
550
                }
19,836✔
551
                else {
19,836✔
552
                    if (compare_right == Compare::All) {
19,836✔
553
                        return not_found;
1,308✔
554
                    }
1,308✔
555
                }
19,836✔
556
            }
24,570✔
557
            if (compare_right == Compare::None || compare_right == Compare::All) {
125,617✔
558
                return 0; // either none or all
2,088✔
559
            }
2,088✔
560
        }
9,240✔
561
        else {
240,005✔
562
            auto right_matches = [&](const QueryValue& left_val) {
270,065✔
563
                for (size_t i = 0; i < right_size; i++) {
500,355✔
564
                    if (c(left_val, right[i])) {
359,061✔
565
                        // match
65,131✔
566
                        if (compare_right == Compare::Any) {
130,265✔
567
                            return true;
127,839✔
568
                        }
127,839✔
569
                        if (compare_right == Compare::None) {
2,426!
570
                            return false; // one matched
278✔
571
                        }
278✔
572
                    }
228,796✔
573
                    else {
228,796✔
574
                        // no match
114,386✔
575
                        if (compare_right == Compare::All) {
228,796✔
576
                            return false;
654✔
577
                        }
654✔
578
                    }
228,796✔
579
                }
359,061✔
580
                return compare_right == Compare::None || compare_right == Compare::All;
205,678✔
581
            };
270,065✔
582

119,953✔
583
            for (size_t i = 0; i < left_size; i++) {
384,317✔
584
                if (right_matches(left[i])) {
270,068!
585
                    if (compare_left == Compare::Any) {
128,663✔
586
                        return 0;
104,482✔
587
                    }
104,482✔
588
                    if (compare_left == ExpressionComparisonType::None) {
24,181✔
589
                        return not_found; // one matched
10,092✔
590
                    }
10,092✔
591
                }
141,405✔
592
                else {
141,405✔
593
                    if (compare_left == ExpressionComparisonType::All) {
141,405✔
594
                        return not_found;
11,182✔
595
                    }
11,182✔
596
                }
141,405✔
597
            }
270,068✔
598
            if (compare_left == ExpressionComparisonType::None || compare_left == ExpressionComparisonType::All) {
177,127✔
599
                return 0; // either none or all
23,466✔
600
            }
23,466✔
601
        }
90,783✔
602

45,342✔
603
        return not_found; // no match
100,023✔
604
    }
100,023✔
605

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

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

616
    void resize(size_t size)
617
    {
8,452,497✔
618
        if (size == m_size)
8,452,497✔
619
            return;
4,640,475✔
620

1,942,224✔
621
        dealloc();
3,812,022✔
622
        m_size = size;
3,812,022✔
623
        if (m_size > 0) {
3,812,022✔
624
            if (m_size > prealloc)
3,789,264✔
625
                m_first = new QueryValue[m_size];
16,080✔
626
            else
3,773,184✔
627
                m_first = &m_cache[0];
3,773,184✔
628
        }
3,789,264✔
629
    }
3,812,022✔
630
    void dealloc()
631
    {
13,809,594✔
632
        if (m_first) {
13,809,594✔
633
            if (m_size > prealloc)
13,778,007✔
634
                delete[] m_first;
16,080✔
635
            m_first = nullptr;
13,778,007✔
636
        }
13,778,007✔
637
    }
13,809,594✔
638
    void fill(const QueryValue& val)
639
    {
×
640
        for (size_t i = 0; i < m_size; i++) {
×
641
            m_first[i] = val;
×
642
        }
×
643
    }
×
644
};
645

646
class Expression {
647
public:
648
    virtual ~Expression() = default;
126,684✔
649

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

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

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

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

671
class Subexpr {
672
public:
673
    virtual ~Subexpr() = default;
7,704,891✔
674

675
    virtual std::unique_ptr<Subexpr> clone() const = 0;
676

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

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

688
    virtual void set_cluster(const Cluster*) {}
81,762✔
689

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

698
    virtual void collect_dependencies(std::vector<TableKey>&) const {}
3,228✔
699

700
    virtual bool has_constant_evaluation() const
701
    {
138,828✔
702
        return false;
138,828✔
703
    }
138,828✔
704

705
    virtual bool has_single_value() const
706
    {
84,450✔
707
        return false;
84,450✔
708
    }
84,450✔
709

710
    virtual bool has_multiple_values() const
711
    {
×
712
        return false;
×
713
    }
×
714

715
    virtual bool has_search_index() const
716
    {
23,934✔
717
        return false;
23,934✔
718
    }
23,934✔
719

720
    virtual bool has_indexes_in_link_map() const
721
    {
×
722
        return false;
×
723
    }
×
724

725
    virtual std::vector<ObjKey> find_all(Mixed) const
726
    {
×
727
        return {};
×
728
    }
×
729

730
    virtual ConstTableRef get_target_table() const
731
    {
26,550✔
732
        return {};
26,550✔
733
    }
26,550✔
734

735
    virtual DataType get_type() const = 0;
736

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

744
    virtual Mixed get_mixed() const
745
    {
×
746
        return {};
×
747
    }
×
748

749
    virtual util::Optional<ExpressionComparisonType> get_comparison_type() const
750
    {
978,330✔
751
        return util::none;
978,330✔
752
    }
978,330✔
753
};
754

755
template <typename T, typename... Args>
756
std::unique_ptr<Subexpr> make_subexpr(Args&&... args)
757
{
106,968✔
758
    return std::unique_ptr<Subexpr>(new T(std::forward<Args>(args)...));
106,968✔
759
}
106,968✔
760

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

782

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

16,003✔
793
    constexpr const bool supported_by_old_query_engine =
32,006✔
794
        (std::numeric_limits<L>::is_integer && std::numeric_limits<R>::is_integer) || std::is_same_v<R, Mixed> ||
32,006✔
795
        (std::is_same_v<L, R> &&
16,003✔
796
         realm::is_any_v<L, double, float, Timestamp, StringData, BinaryData, ObjectId, UUID>);
×
797

16,003✔
798
    if constexpr (REALM_OLDQUERY_FALLBACK && supported_by_old_query_engine) {
32,006✔
799
        const Columns<R>* column = dynamic_cast<const Columns<R>*>(&right);
28,606✔
800
        // TODO: recognize size operator expressions
14,303✔
801
        // auto size_operator = dynamic_cast<const SizeOperator<Size<StringData>, Subexpr>*>(&right);
14,303✔
802

14,303✔
803
        if (column && !column->links_exist() && !column->has_path()) {
28,606!
804
            ConstTableRef t = column->get_base_table();
21,424✔
805
            Query q(t);
21,424✔
806

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

3,591✔
848
    // Return query_expression.hpp node
3,591✔
849
    if constexpr (std::is_same_v<L, TypeOfValue>) {
10,582✔
850
        return make_expression<Compare<Cond>>(make_subexpr<Value<TypeOfValue>>(left), right.clone());
10,560✔
851
    }
10,560✔
852
    else {
10,560✔
853
        return make_expression<Compare<Cond>>(make_subexpr<ConstantMixedValue>(left), right.clone());
10,560✔
854
    }
10,560✔
855
}
7,182✔
856

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

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

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

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

968

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

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

1020
template <class Operator>
1021
Query compare(const Subexpr2<Link>& left, const Obj& obj);
1022
template <class Operator>
1023
Query compare(const Subexpr2<Link>& left, null obj);
1024

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

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

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

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

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

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

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

1152
    using T = Mixed; // used inside the following macros for operator overloads
1153
};
1154

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

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

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

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

1204

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

1226

1227
// Stores N values of type T. Can also exchange data with other ValueBase of different types
1228
template <class T>
1229
class Value : public ValueBase, public Subexpr2<T> {
1230
public:
1231
    Value() = default;
4,633,431✔
1232

1233
    Value(T init)
1234
        : ValueBase(QueryValue(init))
1235
    {
527,351✔
1236
    }
527,351✔
1237

1238
    std::string value_to_string(size_t ndx, util::serializer::SerialisationState& state) const
1239
    {
14,826✔
1240
        auto val = get(ndx);
14,826✔
1241
        if (val.is_null())
14,826✔
1242
            return "NULL";
994✔
1243
        else {
13,832✔
1244
            static_cast<void>(state);
13,832✔
1245
            if constexpr (std::is_same_v<T, TypeOfValue>) {
13,832✔
1246
                return util::serializer::print_value(val.get_type_of_value());
13,580✔
1247
            }
13,580✔
1248
            else if constexpr (std::is_same_v<T, ObjKey>) {
13,580✔
1249
                ObjKey obj_key = val.template get<ObjKey>();
13,544✔
1250
                if (state.target_table) {
8,316✔
1251
                    ObjLink link(state.target_table->get_key(), obj_key);
3,088✔
1252
                    return util::serializer::print_value(link, state.group);
3,088✔
1253
                }
3,088✔
1254
                else {
3,052✔
1255
                    return util::serializer::print_value(obj_key);
3,052✔
1256
                }
3,052✔
1257
            }
3,088✔
1258
            else if constexpr (std::is_same_v<T, ObjLink>) {
13,544✔
1259
                return util::serializer::print_value(val.template get<ObjLink>(), state.group);
13,540✔
1260
            }
13,540✔
1261
            else if constexpr (std::is_same_v<T, Mixed>) {
13,540✔
1262
                if (val.is_type(type_TypedLink)) {
6,770✔
1263
                    return util::serializer::print_value(val.template get<ObjLink>(), state.group);
6✔
1264
                }
6✔
1265
                else {
1,986✔
1266
                    return util::serializer::print_value(val);
1,986✔
1267
                }
1,986✔
1268
            }
1,992✔
1269
            else {
11,548✔
1270
                return util::serializer::print_value(val.template get<T>());
11,548✔
1271
            }
11,548✔
1272
        }
13,832✔
1273
    }
14,826✔
1274

1275
    std::string description(util::serializer::SerialisationState& state) const override
1276
    {
13,688✔
1277
        const size_t sz = size();
13,688✔
1278
        if (m_from_list) {
13,688✔
1279
            std::string desc = state.describe_expression_type(m_comparison_type) + "{";
850✔
1280
            for (size_t i = 0; i < sz; ++i) {
2,838!
1281
                if (i != 0) {
1,988!
1282
                    desc += ", ";
1,246✔
1283
                }
1,246✔
1284
                desc += value_to_string(i, state);
1,988✔
1285
            }
1,988✔
1286
            desc += "}";
850✔
1287
            return desc;
850✔
1288
        }
850✔
1289
        else if (sz == 1) {
12,838✔
1290
            return value_to_string(0, state);
12,838✔
1291
        }
12,838✔
1292
        return "";
×
1293
    }
×
1294

1295
    bool has_multiple_values() const override
1296
    {
666✔
1297
        return m_from_list;
666✔
1298
    }
666✔
1299

1300
    bool has_single_value() const override
1301
    {
551,676✔
1302
        return size() == 1;
551,676✔
1303
    }
551,676✔
1304

1305
    bool has_constant_evaluation() const override
1306
    {
96,673✔
1307
        return true;
96,673✔
1308
    }
96,673✔
1309

1310
    util::Optional<ExpressionComparisonType> get_comparison_type() const final
1311
    {
1,426,512✔
1312
        REALM_ASSERT_DEBUG(!m_comparison_type || m_from_list);
1,426,512!
1313
        return m_comparison_type;
1,426,512✔
1314
    }
1,426,512✔
1315

1316
    void set_comparison_type(util::Optional<ExpressionComparisonType> type)
1317
    {
1,596✔
1318
        m_comparison_type = type;
1,596✔
1319
    }
1,596✔
1320

1321
    Mixed get_mixed() const override
1322
    {
506,330✔
1323
        return get(0);
506,330✔
1324
    }
506,330✔
1325

1326
    void evaluate(size_t, ValueBase& destination) override
1327
    {
120✔
1328
        destination = *this;
120✔
1329
    }
120✔
1330

1331
    std::unique_ptr<Subexpr> clone() const override
1332
    {
24,906✔
1333
        return make_subexpr<Value<T>>(*this);
24,906✔
1334
    }
24,906✔
1335

1336
protected:
1337
    util::Optional<ExpressionComparisonType> m_comparison_type;
1338
};
1339

1340
class ConstantMixedValue : public Value<Mixed> {
1341
public:
1342
    ConstantMixedValue(const Mixed& val)
1343
        : Value(val)
1344
    {
22,908✔
1345
        begin()->use_buffer(m_buffer);
22,908✔
1346
    }
22,908✔
1347

1348
    std::unique_ptr<Subexpr> clone() const override
1349
    {
15,798✔
1350
        return std::unique_ptr<Subexpr>(new ConstantMixedValue(*this));
15,798✔
1351
    }
15,798✔
1352

1353
private:
1354
    ConstantMixedValue(const ConstantMixedValue& other)
1355
        : Value(other)
1356
    {
15,798✔
1357
        begin()->use_buffer(m_buffer);
15,798✔
1358
    }
15,798✔
1359

1360
    std::string m_buffer;
1361
};
1362

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

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

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

1392
    std::vector<std::string> m_buffer;
1393
};
1394

1395
class ConstantStringValue : public Value<StringData> {
1396
public:
1397
    ConstantStringValue(const StringData& string)
1398
        : Value()
1399
        , m_string(string.is_null() ? util::none : util::make_optional(std::string(string)))
1400
    {
6,700✔
1401
        if (m_string)
6,700✔
1402
            set(0, *m_string);
6,380✔
1403
    }
6,700✔
1404

1405
    std::unique_ptr<Subexpr> clone() const override
1406
    {
3,172✔
1407
        return std::unique_ptr<Subexpr>(new ConstantStringValue(*this));
3,172✔
1408
    }
3,172✔
1409

1410
private:
1411
    ConstantStringValue(const ConstantStringValue& other)
1412
        : Value()
1413
        , m_string(other.m_string)
1414
    {
3,172✔
1415
        if (m_string)
3,172✔
1416
            set(0, *m_string);
3,044✔
1417
    }
3,172✔
1418

1419
    util::Optional<std::string> m_string;
1420
};
1421

1422
class ConstantBinaryValue : public Value<BinaryData> {
1423
public:
1424
    ConstantBinaryValue(const BinaryData& bin)
1425
        : Value()
1426
        , m_buffer(bin)
1427
    {
1,960✔
1428
        if (m_buffer.data())
1,960✔
1429
            set(0, BinaryData(m_buffer.data(), m_buffer.size()));
1,960✔
1430
    }
1,960✔
1431

1432
    std::unique_ptr<Subexpr> clone() const override
1433
    {
352✔
1434
        return std::unique_ptr<Subexpr>(new ConstantBinaryValue(*this));
352✔
1435
    }
352✔
1436

1437
private:
1438
    ConstantBinaryValue(const ConstantBinaryValue& other)
1439
        : Value()
1440
        , m_buffer(other.m_buffer)
1441
    {
352✔
1442
        if (m_buffer.data())
352✔
1443
            set(0, BinaryData(m_buffer.data(), m_buffer.size()));
352✔
1444
    }
352✔
1445

1446
    OwnedBinaryData m_buffer;
1447
};
1448

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

1461
    std::unique_ptr<Subexpr> clone() const override
1462
    {
×
1463
        return std::unique_ptr<Subexpr>(new ConstantGeospatialValue(*this));
×
1464
    }
×
1465

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

1479
// Classes used for LinkMap (see below).
1480

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

1485
using LinkMapFunction = util::FunctionRef<bool(ObjKey)>;
1486

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

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

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

1498
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
1499
found the first link that points to key '5'. Other solutions could be a std::vector<ColKey> harvest_all_links(), or an
1500
iterator pattern. First solution can't exit, second solution requires internal state.
1501
*/
1502
class LinkMap final {
1503
public:
1504
    LinkMap() = default;
312✔
1505
    LinkMap(ConstTableRef table, std::vector<ExtendedColumnKey> columns)
1506
        : m_link_column_keys(std::move(columns))
1507
    {
537,776✔
1508
        set_base_table(table);
537,776✔
1509
    }
537,776✔
1510

1511
    LinkMap(LinkMap const& other)
1512
    {
169,278✔
1513
        m_link_column_keys = other.m_link_column_keys;
169,278✔
1514
        m_tables = other.m_tables;
169,278✔
1515
        m_link_types = other.m_link_types;
169,278✔
1516
        m_only_unary_links = other.m_only_unary_links;
169,278✔
1517
    }
169,278✔
1518

1519
    size_t get_nb_hops() const
1520
    {
2,024✔
1521
        return m_link_column_keys.size();
2,024✔
1522
    }
2,024✔
1523

1524
    bool has_links() const
1525
    {
4,982,631✔
1526
        return m_link_column_keys.size() > 0;
4,982,631✔
1527
    }
4,982,631✔
1528
    bool has_indexes() const
1529
    {
976✔
1530
        for (auto& k : m_link_column_keys) {
1,216✔
1531
            if (k.has_index())
1,216✔
1532
                return true;
64✔
1533
        }
1,216✔
1534
        return false;
944✔
1535
    }
976✔
1536

1537
    ColKey get_first_column_key() const
1538
    {
280✔
1539
        REALM_ASSERT(has_links());
280✔
1540
        return m_link_column_keys[0];
280✔
1541
    }
280✔
1542

1543
    void set_base_table(ConstTableRef table);
1544

1545
    void set_cluster(const Cluster* cluster)
1546
    {
30,714✔
1547
        Allocator& alloc = get_base_table()->get_alloc();
30,714✔
1548
        ArrayPayload* array_ptr;
30,714✔
1549
        switch (m_link_types[0]) {
30,714✔
1550
            case col_type_Link:
15,918✔
1551
                if (m_link_column_keys[0].is_dictionary()) {
15,918✔
1552
                    array_ptr = &m_leaf.emplace<ArrayInteger>(alloc);
324✔
1553
                }
324✔
1554
                else {
15,594✔
1555
                    array_ptr = &m_leaf.emplace<ArrayKey>(alloc);
15,594✔
1556
                }
15,594✔
1557
                break;
15,918✔
1558
            case col_type_LinkList:
13,902✔
1559
                array_ptr = &m_leaf.emplace<ArrayList>(alloc);
13,902✔
1560
                break;
13,902✔
1561
            case col_type_BackLink:
894✔
1562
                array_ptr = &m_leaf.emplace<ArrayBacklink>(alloc);
894✔
1563
                break;
894✔
1564
            default:
✔
1565
                REALM_UNREACHABLE();
×
1566
        }
30,714✔
1567
        cluster->init_leaf(m_link_column_keys[0], array_ptr);
30,714✔
1568
    }
30,714✔
1569

1570
    void collect_dependencies(std::vector<TableKey>& tables) const;
1571

1572
    std::string description(util::serializer::SerialisationState& state) const;
1573

1574
    ObjKey get_unary_link_or_not_found(size_t index) const
1575
    {
55,244✔
1576
        REALM_ASSERT(m_only_unary_links);
55,244✔
1577
        ObjKey result;
55,244✔
1578
        map_links(index, [&](ObjKey key) {
54,552✔
1579
            result = key;
53,860✔
1580
            return false; // exit search, only one result ever expected
53,860✔
1581
        });
53,860✔
1582
        return result;
55,244✔
1583
    }
55,244✔
1584

1585
    std::vector<ObjKey> get_links(size_t index) const
1586
    {
246,330✔
1587
        std::vector<ObjKey> res;
246,330✔
1588
        get_links(index, res);
246,330✔
1589
        return res;
246,330✔
1590
    }
246,330✔
1591

1592
    std::vector<ObjKey> get_origin_objkeys(ObjKey key, size_t column = 0) const;
1593

1594
    size_t count_links(size_t row) const
1595
    {
22,830✔
1596
        size_t count = 0;
22,830✔
1597
        map_links(row, [&](ObjKey) {
19,917✔
1598
            ++count;
17,004✔
1599
            return true;
17,004✔
1600
        });
17,004✔
1601
        return count;
22,830✔
1602
    }
22,830✔
1603

1604
    size_t count_all_backlinks(size_t row) const
1605
    {
184✔
1606
        size_t count = 0;
184✔
1607
        auto table = get_target_table().unchecked_ptr();
184✔
1608
        map_links(row, [&](ObjKey key) {
432✔
1609
            count += table->get_object(key).get_backlink_count();
432✔
1610
            return true;
432✔
1611
        });
432✔
1612
        return count;
184✔
1613
    }
184✔
1614

1615
    void map_links(size_t row, LinkMapFunction lm) const
1616
    {
364,650✔
1617
        map_links(0, row, lm);
364,650✔
1618
    }
364,650✔
1619

1620
    bool only_unary_links() const
1621
    {
309,753✔
1622
        return m_only_unary_links;
309,753✔
1623
    }
309,753✔
1624

1625
    ConstTableRef get_base_table() const
1626
    {
1,324,776✔
1627
        return m_tables.empty() ? nullptr : m_tables[0];
1,065,927✔
1628
    }
1,324,776✔
1629

1630
    ConstTableRef get_target_table() const
1631
    {
2,517,420✔
1632
        REALM_ASSERT(!m_tables.empty());
2,517,420✔
1633
        return m_tables.back();
2,517,420✔
1634
    }
2,517,420✔
1635

1636
    bool links_exist() const
1637
    {
20,886✔
1638
        return !m_link_column_keys.empty();
20,886✔
1639
    }
20,886✔
1640

1641
    ColKey pop_last()
1642
    {
44✔
1643
        ColKey col = m_link_column_keys.back();
44✔
1644
        m_link_column_keys.pop_back();
44✔
1645
        m_tables.pop_back();
44✔
1646
        return col;
44✔
1647
    }
44✔
1648

1649
private:
1650
    bool map_links(size_t column, ObjKey key, LinkMapFunction lm) const;
1651
    void map_links(size_t column, size_t row, LinkMapFunction lm) const;
1652

1653
    void get_links(size_t row, std::vector<ObjKey>& result) const
1654
    {
246,321✔
1655
        map_links(row, [&](ObjKey key) {
354,981✔
1656
            result.push_back(key);
354,981✔
1657
            return true; // continue evaluation
354,981✔
1658
        });
354,981✔
1659
    }
246,321✔
1660

1661
    mutable std::vector<ExtendedColumnKey> m_link_column_keys;
1662
    std::vector<ColumnType> m_link_types;
1663
    std::vector<ConstTableRef> m_tables;
1664
    bool m_only_unary_links = true;
1665

1666
    mpark::variant<mpark::monostate, ArrayKey, ArrayInteger, ArrayList, ArrayBacklink> m_leaf;
1667

1668
    template <class>
1669
    friend Query compare(const Subexpr2<Link>&, const Obj&);
1670
};
1671

1672
template <class T>
1673
Value<T> make_value_for_link(bool only_unary_links, size_t size)
1674
{
1675
    Value<T> value;
1676
    if (only_unary_links) {
1677
        REALM_ASSERT(size <= 1);
1678
        value.init(false, 1);
1679
        value.m_storage.set_null(0);
1680
    }
1681
    else {
1682
        value.init(true, size);
1683
    }
1684
    return value;
1685
}
1686

1687
// This class can be used as untyped base for expressions that handle object properties
1688
class ObjPropertyBase {
1689
public:
1690
    ObjPropertyBase(ColKey column, ConstTableRef table, std::vector<ExtendedColumnKey> links,
1691
                    util::Optional<ExpressionComparisonType> type)
1692
        : m_link_map(table, std::move(links))
1693
        , m_column_key(column)
1694
        , m_comparison_type(type)
1695
    {
507,302✔
1696
    }
507,302✔
1697
    ObjPropertyBase(const ObjPropertyBase& other)
1698
        : m_link_map(other.m_link_map)
1699
        , m_column_key(other.m_column_key)
1700
        , m_comparison_type(other.m_comparison_type)
1701
    {
46,424✔
1702
    }
46,424✔
1703
    ObjPropertyBase(ColKey column, const LinkMap& link_map, util::Optional<ExpressionComparisonType> type)
1704
        : m_link_map(link_map)
1705
        , m_column_key(column)
1706
        , m_comparison_type(type)
1707
    {
×
1708
    }
×
1709

1710
    bool links_exist() const
1711
    {
2,868,297✔
1712
        return m_link_map.has_links();
2,868,297✔
1713
    }
2,868,297✔
1714

1715
    bool only_unary_links() const
1716
    {
×
1717
        return m_link_map.only_unary_links();
×
1718
    }
×
1719

1720
    bool is_nullable() const
1721
    {
1,118,728✔
1722
        return m_column_key.get_attrs().test(col_attr_Nullable);
1,118,728✔
1723
    }
1,118,728✔
1724

1725
    const LinkMap& get_link_map() const
1726
    {
12✔
1727
        return m_link_map;
12✔
1728
    }
12✔
1729

1730
    ColKey column_key() const noexcept
1731
    {
724,278✔
1732
        return m_column_key;
724,278✔
1733
    }
724,278✔
1734

1735
    virtual bool has_path() const noexcept
1736
    {
468,484✔
1737
        return false;
468,484✔
1738
    }
468,484✔
1739

1740
protected:
1741
    LinkMap m_link_map;
1742
    // Column index of payload column of m_table
1743
    mutable ColKey m_column_key;
1744
    util::Optional<ExpressionComparisonType> m_comparison_type; // Any, All, None
1745
};
1746

1747
// Combines Subexpr2<T> and ObjPropertyBase
1748
// Implements virtual functions defined in Expression/Subexpr
1749
template <class T>
1750
class ObjPropertyExpr : public Subexpr2<T>, public ObjPropertyBase {
1751
public:
1752
    using ObjPropertyBase::ObjPropertyBase;
1753

1754
    bool has_multiple_values() const override
1755
    {
184✔
1756
        return m_link_map.has_links() && !m_link_map.only_unary_links();
184!
1757
    }
184✔
1758

1759
    ConstTableRef get_base_table() const final
1760
    {
121,722✔
1761
        return m_link_map.get_base_table();
121,722✔
1762
    }
121,722✔
1763

1764
    void set_base_table(ConstTableRef table) final
1765
    {
27,732✔
1766
        if (table != get_base_table()) {
27,732✔
1767
            m_link_map.set_base_table(table);
3,152✔
1768
        }
3,152✔
1769
    }
27,732✔
1770

1771
    bool has_search_index() const final
1772
    {
7,412✔
1773
        auto target_table = m_link_map.get_target_table();
7,412✔
1774
        return target_table->search_index_type(m_column_key) == IndexType::General;
7,412✔
1775
    }
7,412✔
1776

1777
    bool has_indexes_in_link_map() const final
1778
    {
976✔
1779
        return m_link_map.has_indexes();
976✔
1780
    }
976✔
1781

1782
    std::vector<ObjKey> find_all(Mixed value) const final
1783
    {
688✔
1784
        std::vector<ObjKey> ret;
688✔
1785
        std::vector<ObjKey> result;
688✔
1786

344✔
1787
        if (value.is_null() && !m_column_key.is_nullable()) {
688!
1788
            return ret;
20✔
1789
        }
20✔
1790

334✔
1791
        if (m_link_map.get_target_table()->get_primary_key_column() == m_column_key) {
668!
1792
            // Only one object with a given key would be possible
4✔
1793
            if (auto k = m_link_map.get_target_table()->find_primary_key(value))
8!
1794
                result.push_back(k);
8✔
1795
        }
8✔
1796
        else {
660✔
1797
            SearchIndex* index = m_link_map.get_target_table()->get_search_index(m_column_key);
660✔
1798
            REALM_ASSERT(index);
660!
1799
            if (value.is_null()) {
660!
1800
                index->find_all(result, realm::null{});
24✔
1801
            }
24✔
1802
            else {
636✔
1803
                T val = value.get<T>();
636✔
1804
                index->find_all(result, val);
636✔
1805
            }
636✔
1806
        }
660✔
1807

334✔
1808
        for (ObjKey k : result) {
10,096!
1809
            auto ndxs = m_link_map.get_origin_objkeys(k);
10,096✔
1810
            ret.insert(ret.end(), ndxs.begin(), ndxs.end());
10,096✔
1811
        }
10,096✔
1812

334✔
1813
        return ret;
668✔
1814
    }
668✔
1815

1816
    void collect_dependencies(std::vector<TableKey>& tables) const final
1817
    {
692✔
1818
        m_link_map.collect_dependencies(tables);
692✔
1819
    }
692✔
1820

1821
    std::string description(util::serializer::SerialisationState& state) const override
1822
    {
1,996✔
1823
        return state.describe_expression_type(m_comparison_type) + state.describe_columns(m_link_map, m_column_key);
1,996✔
1824
    }
1,996✔
1825

1826
    util::Optional<ExpressionComparisonType> get_comparison_type() const final
1827
    {
539,678✔
1828
        return m_comparison_type;
539,678✔
1829
    }
539,678✔
1830

1831
    std::unique_ptr<Subexpr> clone() const override
1832
    {
34,080✔
1833
        return make_subexpr<Columns<T>>(static_cast<const Columns<T>&>(*this));
34,080✔
1834
    }
34,080✔
1835
};
1836

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

1844
template <class T>
1845
class SimpleQuerySupport : public ObjPropertyExpr<T> {
1846
public:
1847
    using ObjPropertyExpr<T>::links_exist;
1848

1849
    SimpleQuerySupport(ColKey column, ConstTableRef table, const std::vector<ExtendedColumnKey>& links = {},
1850
                       util::Optional<ExpressionComparisonType> type = util::none)
1851
        : ObjPropertyExpr<T>(column, table, links, type)
1852
    {
463,180✔
1853
    }
463,180✔
1854

1855
    void set_cluster(const Cluster* cluster) override
1856
    {
18,536✔
1857
        if (links_exist()) {
18,536✔
1858
            m_link_map.set_cluster(cluster);
4,360✔
1859
        }
4,360✔
1860
        else {
14,176✔
1861
            m_leaf.emplace(m_link_map.get_base_table()->get_alloc());
14,176✔
1862
            cluster->init_leaf(m_column_key, &*m_leaf);
14,176✔
1863
        }
14,176✔
1864
    }
18,536✔
1865

1866
    void evaluate(size_t index, ValueBase& destination) override
1867
    {
310,208✔
1868
        if (links_exist()) {
310,208✔
1869
            REALM_ASSERT(!m_leaf);
40,672✔
1870

20,336✔
1871
            if (m_link_map.only_unary_links()) {
40,672✔
1872
                REALM_ASSERT(destination.size() == 1);
35,108✔
1873
                REALM_ASSERT(!destination.m_from_list);
35,108✔
1874
                destination.set_null(0);
35,108✔
1875
                auto link_translation_key = this->m_link_map.get_unary_link_or_not_found(index);
35,108✔
1876
                if (link_translation_key) {
35,108✔
1877
                    const Obj obj = m_link_map.get_target_table()->get_object(link_translation_key);
34,172✔
1878
                    if constexpr (realm::is_any_v<T, ObjectId, UUID>) {
34,172✔
1879
                        auto opt_val = obj.get<util::Optional<T>>(m_column_key);
16,124✔
1880
                        if (opt_val) {
18,048✔
1881
                            destination.set(0, *opt_val);
2,208✔
1882
                        }
2,208✔
1883
                        else {
15,840✔
1884
                            destination.set_null(0);
15,840✔
1885
                        }
15,840✔
1886
                    }
18,048✔
1887
                    else {
16,124✔
1888
                        destination.set(0, obj.get<T>(m_column_key));
16,124✔
1889
                    }
16,124✔
1890
                }
34,172✔
1891
            }
35,108✔
1892
            else {
5,564✔
1893
                std::vector<ObjKey> links = m_link_map.get_links(index);
5,564✔
1894
                destination.init(true, links.size());
5,564✔
1895
                for (size_t t = 0; t < links.size(); t++) {
42,624!
1896
                    const Obj obj = m_link_map.get_target_table()->get_object(links[t]);
37,060✔
1897
                    if constexpr (realm::is_any_v<T, ObjectId, UUID>) {
37,060✔
1898
                        auto opt_val = obj.get<util::Optional<T>>(m_column_key);
25,060✔
1899
                        if (opt_val) {
18,530✔
1900
                            destination.set(t, *opt_val);
408✔
1901
                        }
408✔
1902
                        else {
11,592✔
1903
                            destination.set_null(t);
11,592✔
1904
                        }
11,592✔
1905
                    }
12,000✔
1906
                    else {
25,060✔
1907
                        destination.set(t, obj.get<T>(m_column_key));
25,060✔
1908
                    }
25,060✔
1909
                }
37,060✔
1910
            }
5,564✔
1911
        }
40,672✔
1912
        else {
269,536✔
1913
            // Not a link column
134,768✔
1914
            REALM_ASSERT(m_leaf);
269,536✔
1915
            REALM_ASSERT(destination.size() == 1);
269,536✔
1916
            REALM_ASSERT(!destination.m_from_list);
269,536✔
1917
            if (m_leaf->is_null(index)) {
269,536✔
1918
                destination.set_null(0);
59,724✔
1919
            }
59,724✔
1920
            else {
209,812✔
1921
                destination.set(0, m_leaf->get(index));
209,812✔
1922
            }
209,812✔
1923
        }
269,536✔
1924
    }
310,208✔
1925

1926
    void evaluate(ObjKey key, ValueBase& destination) override
1927
    {
2,320✔
1928
        Value<T>& d = static_cast<Value<T>&>(destination);
2,320✔
1929
        d.set(0, m_link_map.get_target_table()->get_object(key).template get<T>(m_column_key));
2,320✔
1930
    }
2,320✔
1931

1932
    SimpleQuerySupport(const SimpleQuerySupport& other)
1933
        : ObjPropertyExpr<T>(other)
1934
    {
20,996✔
1935
    }
20,996✔
1936

1937
    SizeOperator<T> size()
1938
    {
656✔
1939
        return SizeOperator<T>(this->clone());
656✔
1940
    }
656✔
1941

1942
    TypeOfValueOperator<T> type_of_value()
1943
    {
480✔
1944
        return TypeOfValueOperator<T>(this->clone());
480✔
1945
    }
480✔
1946

1947
private:
1948
    using ObjPropertyExpr<T>::m_link_map;
1949
    using ObjPropertyExpr<T>::m_column_key;
1950

1951
    using LeafType = typename ColumnTypeTraits<T>::cluster_leaf_type;
1952
    std::optional<LeafType> m_leaf;
1953
};
1954

1955
template <>
1956
class Columns<Timestamp> : public SimpleQuerySupport<Timestamp> {
1957
    using SimpleQuerySupport::SimpleQuerySupport;
1958
};
1959

1960
template <>
1961
class Columns<BinaryData> : public SimpleQuerySupport<BinaryData> {
1962
    using SimpleQuerySupport::SimpleQuerySupport;
1963

1964
    friend Query operator==(const Columns<BinaryData>& left, BinaryData right)
1965
    {
316✔
1966
        return create<Equal>(right, left);
316✔
1967
    }
316✔
1968

1969
    friend Query operator==(BinaryData left, const Columns<BinaryData>& right)
1970
    {
4✔
1971
        return create<Equal>(left, right);
4✔
1972
    }
4✔
1973

1974
    friend Query operator!=(const Columns<BinaryData>& left, BinaryData right)
1975
    {
238✔
1976
        return create<NotEqual>(right, left);
238✔
1977
    }
238✔
1978

1979
    friend Query operator!=(BinaryData left, const Columns<BinaryData>& right)
1980
    {
2✔
1981
        return create<NotEqual>(left, right);
2✔
1982
    }
2✔
1983

1984
    friend Query operator==(const Columns<BinaryData>& left, realm::null)
1985
    {
12✔
1986
        return create<Equal>(BinaryData(), left);
12✔
1987
    }
12✔
1988

1989
    friend Query operator==(realm::null, const Columns<BinaryData>& right)
1990
    {
×
1991
        return create<Equal>(BinaryData(), right);
×
1992
    }
×
1993

1994
    friend Query operator!=(const Columns<BinaryData>& left, realm::null)
1995
    {
×
1996
        return create<NotEqual>(BinaryData(), left);
×
1997
    }
×
1998

1999
    friend Query operator!=(realm::null, const Columns<BinaryData>& right)
2000
    {
×
2001
        return create<NotEqual>(BinaryData(), right);
×
2002
    }
×
2003
};
2004

2005
template <>
2006
class Columns<ObjectId> : public SimpleQuerySupport<ObjectId> {
2007
    using SimpleQuerySupport::SimpleQuerySupport;
2008
};
2009

2010
template <>
2011
class Columns<Decimal128> : public SimpleQuerySupport<Decimal128> {
2012
    using SimpleQuerySupport::SimpleQuerySupport;
2013
};
2014

2015
template <>
2016
class Columns<Mixed> : public SimpleQuerySupport<Mixed> {
2017
public:
2018
    using SimpleQuerySupport::SimpleQuerySupport;
2019
    void evaluate(size_t index, ValueBase& destination) override
2020
    {
37,784✔
2021
        destination.init(false, 1); // Size of 1 is expected by SimpleQuerySupport
37,784✔
2022
        SimpleQuerySupport::evaluate(index, destination);
37,784✔
2023
        if (m_path.size() > 0) {
37,784✔
2024
            if (auto sz = destination.size()) {
312✔
2025
                Collection::QueryCtrlBlock ctrl(m_path, *get_base_table(),
264✔
2026
                                                destination.m_from_list || !m_path_only_unary_keys);
264✔
2027
                for (size_t i = 0; i < sz; i++) {
576✔
2028
                    Collection::get_any(ctrl, destination.get(i), 0);
312✔
2029
                }
312✔
2030
                sz = ctrl.matches.size();
264✔
2031
                destination.init(ctrl.from_list || sz == 0, sz);
264✔
2032
                destination.set(ctrl.matches.begin(), ctrl.matches.end());
264✔
2033
            }
264✔
2034
        }
312✔
2035
    }
37,784✔
2036
    std::string description(util::serializer::SerialisationState& state) const override
2037
    {
368✔
2038
        return ObjPropertyExpr::description(state) + util::to_string(m_path);
368✔
2039
    }
368✔
2040
    Columns<Mixed>& path(const Path& path)
2041
    {
132✔
2042
        for (auto& elem : path) {
340✔
2043
            if (elem.is_all()) {
340✔
2044
                m_path_only_unary_keys = false;
72✔
2045
            }
72✔
2046
            m_path.emplace_back(elem);
340✔
2047
        }
340✔
2048
        return *this;
132✔
2049
    }
132✔
2050
    bool has_path() const noexcept override
2051
    {
448✔
2052
        return !m_path.empty();
448✔
2053
    }
448✔
2054

2055
private:
2056
    Path m_path;
2057
    bool m_path_only_unary_keys = true;
2058
};
2059

2060
template <>
2061
class Columns<UUID> : public SimpleQuerySupport<UUID> {
2062
    using SimpleQuerySupport::SimpleQuerySupport;
2063
};
2064

2065
template <class T, class S, class I>
2066
inline std::enable_if_t<!realm::is_any_v<T, StringData, realm::null, const char*, std::string>, Query>
2067
string_compare(const Subexpr2<StringData>& left, T right, bool)
2068
{
×
2069
    return make_expression<Compare<Equal>>(right.clone(), left.clone());
×
2070
}
×
2071

2072
template <class T, class S, class I>
2073
inline std::enable_if_t<realm::is_any_v<T, StringData, realm::null, const char*, std::string>, Query>
2074
string_compare(const Subexpr2<StringData>& left, T right, bool case_sensitive)
2075
{
12,334✔
2076
    StringData sd(right);
12,334✔
2077
    if (case_sensitive)
12,334✔
2078
        return create<S>(sd, left);
7,990✔
2079
    else
4,344✔
2080
        return create<I>(sd, left);
4,344✔
2081
}
12,334✔
2082

2083
template <class S, class I>
2084
Query string_compare(const Subexpr2<StringData>& left, const Subexpr2<StringData>& right, bool case_sensitive)
2085
{
3,960✔
2086
    if (case_sensitive)
3,960✔
2087
        return make_expression<Compare<S>>(right.clone(), left.clone());
2,268✔
2088
    else
1,692✔
2089
        return make_expression<Compare<I>>(right.clone(), left.clone());
1,692✔
2090
}
3,960✔
2091

2092
template <>
2093
class Columns<StringData> : public SimpleQuerySupport<StringData> {
2094
public:
2095
    Columns(ColKey column, ConstTableRef table, const std::vector<ExtendedColumnKey>& links = {},
2096
            util::Optional<ExpressionComparisonType> type = util::none)
2097
        : SimpleQuerySupport(column, table, links, type)
2098
    {
18,856✔
2099
    }
18,856✔
2100

2101
    Columns(Columns const& other)
2102
        : SimpleQuerySupport(other)
2103
    {
12,564✔
2104
    }
12,564✔
2105

2106
    Columns(Columns&& other) noexcept
2107
        : SimpleQuerySupport(other)
2108
    {
×
2109
    }
×
2110

2111
    Query fulltext(StringData sd) const;
2112

2113
    using SimpleQuerySupport::size;
2114

2115
    // Columns<String> == Columns<String>
2116
    friend Query operator==(const Columns<StringData>& left, const Columns<StringData>& right)
2117
    {
192✔
2118
        return string_compare<Equal, EqualIns>(left, right, true);
192✔
2119
    }
192✔
2120

2121
    // Columns<String> != Columns<String>
2122
    friend Query operator!=(const Columns<StringData>& left, const Columns<StringData>& right)
2123
    {
188✔
2124
        return string_compare<NotEqual, NotEqualIns>(left, right, true);
188✔
2125
    }
188✔
2126

2127
    // String == Columns<String>
2128
    template <class T>
2129
    friend Query operator==(T left, const Columns<StringData>& right)
2130
    {
4✔
2131
        return operator==(right, left);
4✔
2132
    }
4✔
2133

2134
    // String != Columns<String>
2135
    template <class T>
2136
    friend Query operator!=(T left, const Columns<StringData>& right)
2137
    {
2✔
2138
        return operator!=(right, left);
2✔
2139
    }
2✔
2140

2141
    // Columns<String> == String
2142
    template <class T>
2143
    friend Query operator==(const Columns<StringData>& left, T right)
2144
    {
1,644✔
2145
        return string_compare<T, Equal, EqualIns>(left, right, true);
1,644✔
2146
    }
1,644✔
2147

2148
    // Columns<String> != String
2149
    template <class T>
2150
    friend Query operator!=(const Columns<StringData>& left, T right)
2151
    {
482✔
2152
        return string_compare<T, NotEqual, NotEqualIns>(left, right, true);
482✔
2153
    }
482✔
2154
};
2155

2156
template <class T, class S, class I>
2157
Query binary_compare(const Subexpr2<BinaryData>& left, T right, bool case_sensitive)
2158
{
×
2159
    BinaryData data(right);
×
2160
    if (case_sensitive)
×
2161
        return create<S>(data, left);
×
2162
    else
×
2163
        return create<I>(data, left);
×
2164
}
×
2165

2166
template <class S, class I>
2167
Query binary_compare(const Subexpr2<BinaryData>& left, const Subexpr2<BinaryData>& right, bool case_sensitive)
2168
{
×
2169
    if (case_sensitive)
×
2170
        return make_expression<Compare<S>>(right.clone(), left.clone());
×
2171
    else
×
2172
        return make_expression<Compare<I>>(right.clone(), left.clone());
×
2173
}
×
2174

2175
template <class T, class S, class I>
2176
Query mixed_compare(const Subexpr2<Mixed>& left, T right, bool case_sensitive)
2177
{
66✔
2178
    Mixed data(right);
66✔
2179
    if (case_sensitive)
66✔
2180
        return create<S>(data, left);
30✔
2181
    else
36✔
2182
        return create<I>(data, left);
36✔
2183
}
66✔
2184

2185
template <class S, class I>
2186
Query mixed_compare(const Subexpr2<Mixed>& left, const Subexpr2<Mixed>& right, bool case_sensitive)
2187
{
×
2188
    if (case_sensitive)
×
2189
        return make_expression<Compare<S>>(right.clone(), left.clone());
×
2190
    else
×
2191
        return make_expression<Compare<I>>(right.clone(), left.clone());
×
2192
}
×
2193

2194

2195
// This class is intended to perform queries on the *pointers* of links, contrary to performing queries on *payload*
2196
// in linked-to tables. Queries can be "find first link that points at row X" or "find first null-link". Currently
2197
// only "find first null link" and "find first non-null link" is supported. More will be added later. When we add
2198
// more, I propose to remove the <bool has_links> template argument from this class and instead template it by
2199
// a criteria-class (like the FindNullLinks class below in find_first()) in some generalized fashion.
2200
template <bool has_links>
2201
class UnaryLinkCompare : public Expression {
2202
public:
2203
    UnaryLinkCompare(const LinkMap& lm)
2204
        : m_link_map(lm)
2205
    {
126✔
2206
    }
126✔
2207

2208
    void set_base_table(ConstTableRef table) override
2209
    {
152✔
2210
        m_link_map.set_base_table(table);
152✔
2211
    }
152✔
2212

2213
    void set_cluster(const Cluster* cluster) override
2214
    {
208✔
2215
        m_link_map.set_cluster(cluster);
208✔
2216
    }
208✔
2217

2218
    void collect_dependencies(std::vector<TableKey>& tables) const override
2219
    {
28✔
2220
        m_link_map.collect_dependencies(tables);
28✔
2221
    }
28✔
2222

2223
    // Return main table of query (table on which table->where()... is invoked). Note that this is not the same as
2224
    // any linked-to payload tables
2225
    ConstTableRef get_base_table() const override
2226
    {
126✔
2227
        return m_link_map.get_base_table();
126✔
2228
    }
126✔
2229

2230
    size_t find_first(size_t start, size_t end) const override
2231
    {
1,574✔
2232
        for (; start < end; ++start) {
3,056✔
2233
            bool found_link = false;
2,926✔
2234
            m_link_map.map_links(start, [&](ObjKey) {
2,214✔
2235
                found_link = true;
1,502✔
2236
                return false; // we've found a key, so this can't be a null-link, so exit link harvesting
1,502✔
2237
            });
1,502✔
2238
            if (found_link == has_links)
2,926✔
2239
                return start;
1,444✔
2240
        }
2,926✔
2241

787✔
2242
        return not_found;
852✔
2243
    }
1,574✔
2244

2245
    std::string description(util::serializer::SerialisationState& state) const override
2246
    {
×
2247
        return state.describe_columns(m_link_map, ColKey()) + (has_links ? " != NULL" : " == NULL");
×
2248
    }
×
2249

2250
    std::unique_ptr<Expression> clone() const override
2251
    {
114✔
2252
        return std::unique_ptr<Expression>(new UnaryLinkCompare(*this));
114✔
2253
    }
114✔
2254

2255
private:
2256
    UnaryLinkCompare(const UnaryLinkCompare& other)
2257
        : Expression(other)
2258
        , m_link_map(other.m_link_map)
2259
    {
114✔
2260
    }
114✔
2261

2262
    mutable LinkMap m_link_map;
2263
};
2264

2265
class LinkCount : public Subexpr2<Int> {
2266
public:
2267
    LinkCount(const LinkMap& link_map)
2268
        : m_link_map(link_map)
2269
    {
852✔
2270
        if (m_link_map.get_nb_hops() > 1) {
852✔
2271
            m_column_key = m_link_map.pop_last();
44✔
2272
        }
44✔
2273
    }
852✔
2274
    LinkCount(LinkCount const& other)
2275
        : Subexpr2<Int>(other)
2276
        , m_link_map(other.m_link_map)
2277
        , m_column_key(other.m_column_key)
2278
    {
2,520✔
2279
    }
2,520✔
2280

2281
    std::unique_ptr<Subexpr> clone() const override
2282
    {
2,520✔
2283
        return make_subexpr<LinkCount>(*this);
2,520✔
2284
    }
2,520✔
2285

2286
    ConstTableRef get_base_table() const override
2287
    {
1,284✔
2288
        return m_link_map.get_base_table();
1,284✔
2289
    }
1,284✔
2290

2291
    void set_base_table(ConstTableRef table) override
2292
    {
1,464✔
2293
        m_link_map.set_base_table(table);
1,464✔
2294
    }
1,464✔
2295

2296
    void set_cluster(const Cluster* cluster) override
2297
    {
1,962✔
2298
        m_link_map.set_cluster(cluster);
1,962✔
2299
    }
1,962✔
2300

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

2306
    void evaluate(size_t index, ValueBase& destination) override;
2307

2308
    std::string description(util::serializer::SerialisationState& state) const override;
2309

2310
private:
2311
    LinkMap m_link_map;
2312
    ColKey m_column_key;
2313
};
2314

2315
// Gives a count of all backlinks across all columns for the specified row.
2316
// The unused template parameter is a hack to avoid a circular dependency between table.hpp and query_expression.hpp.
2317
template <class>
2318
class BacklinkCount : public Subexpr2<Int> {
2319
public:
2320
    BacklinkCount(const LinkMap& link_map)
2321
        : m_link_map(link_map)
2322
    {
2323
    }
2324
    BacklinkCount(LinkMap&& link_map)
2325
        : m_link_map(std::move(link_map))
2326
    {
2327
    }
2328
    BacklinkCount(ConstTableRef table, std::vector<ExtendedColumnKey>&& links = {})
2329
        : m_link_map(table, std::move(links))
2330
    {
304✔
2331
    }
304✔
2332
    BacklinkCount(BacklinkCount const& other)
2333
        : Subexpr2<Int>(other)
2334
        , m_link_map(other.m_link_map)
2335
    {
568✔
2336
    }
568✔
2337

2338
    std::unique_ptr<Subexpr> clone() const override
2339
    {
568✔
2340
        return make_subexpr<BacklinkCount<Int>>(*this);
568✔
2341
    }
568✔
2342

2343
    ConstTableRef get_base_table() const override
2344
    {
264✔
2345
        return m_link_map.get_base_table();
264✔
2346
    }
264✔
2347

2348
    void set_base_table(ConstTableRef table) override
2349
    {
264✔
2350
        m_link_map.set_base_table(table);
264✔
2351
    }
264✔
2352

2353
    void set_cluster(const Cluster* cluster) override
2354
    {
264✔
2355
        if (m_link_map.has_links()) {
264✔
2356
            m_link_map.set_cluster(cluster);
88✔
2357
        }
88✔
2358
        else {
176✔
2359
            m_cluster = cluster;
176✔
2360
        }
176✔
2361
    }
264✔
2362

2363
    void collect_dependencies(std::vector<TableKey>& tables) const override
2364
    {
×
2365
        m_link_map.collect_dependencies(tables);
×
2366
    }
×
2367

2368
    void evaluate(size_t index, ValueBase& destination) override
2369
    {
872✔
2370
        size_t count;
872✔
2371
        if (m_link_map.has_links()) {
872✔
2372
            count = m_link_map.count_all_backlinks(index);
184✔
2373
        }
184✔
2374
        else {
688✔
2375
            const Obj obj = m_link_map.get_base_table()->get_object(m_cluster->get_real_key(index));
688✔
2376
            count = obj.get_backlink_count();
688✔
2377
        }
688✔
2378
        destination = Value<int64_t>(count);
872✔
2379
    }
872✔
2380

2381
    std::string description(util::serializer::SerialisationState& state) const override
2382
    {
132✔
2383
        std::string s;
132✔
2384
        if (m_link_map.links_exist()) {
132✔
2385
            s += state.describe_columns(m_link_map, ColKey()) + util::serializer::value_separator;
44✔
2386
        }
44✔
2387
        s += "@links.@count";
132✔
2388
        return s;
132✔
2389
    }
132✔
2390

2391
private:
2392
    const Cluster* m_cluster = nullptr;
2393
    LinkMap m_link_map;
2394
};
2395

2396
#if REALM_ENABLE_GEOSPATIAL
2397
class GeoWithinCompare : public Expression {
2398
public:
2399
    GeoWithinCompare(const LinkMap& lm, Geospatial&& bounds, util::Optional<ExpressionComparisonType> comp_type)
2400
        : m_link_map(lm)
2401
        , m_bounds(std::move(bounds))
2402
        , m_region(m_bounds)
2403
        , m_comp_type(comp_type)
2404
    {
360✔
2405
        Status status = m_region.get_conversion_status();
360✔
2406
        if (!status.is_ok()) {
360✔
2407
            throw InvalidArgument(status.code(),
32✔
2408
                                  util::format("Invalid region in GEOWITHIN query for parameter '%1': '%2'", m_bounds,
32✔
2409
                                               status.reason()));
32✔
2410
        }
32✔
2411
    }
360✔
2412

2413
    GeoWithinCompare(const GeoWithinCompare& other)
2414
        : m_link_map(other.m_link_map)
2415
        , m_bounds(other.m_bounds)
2416
        , m_region(m_bounds)
2417
        , m_comp_type(other.m_comp_type)
2418
    {
184✔
2419
    }
184✔
2420

2421
    void set_base_table(ConstTableRef table) override
2422
    {
328✔
2423
        m_link_map.set_base_table(table);
328✔
2424
        m_coords_col = m_link_map.get_target_table()->get_column_key(Geospatial::c_geo_point_coords_col_name);
328✔
2425
        m_type_col = m_link_map.get_target_table()->get_column_key(Geospatial::c_geo_point_type_col_name);
328✔
2426
        if (!m_coords_col || !m_type_col || !m_coords_col.is_list() ||
328✔
2427
            m_coords_col.get_type() != ColumnType(ColumnType::Type::Double) ||
326✔
2428
            m_type_col.get_type() != ColumnType(ColumnType::Type::String) || m_type_col.is_collection()) {
326✔
2429
            util::serializer::SerialisationState none;
4✔
2430
            throw std::runtime_error(util::format(
4✔
2431
                "Query '%1' links to data in the wrong format for a geoWithin query", this->description(none)));
4✔
2432
        }
4✔
2433
        if (!m_link_map.get_target_table()->is_embedded()) {
324✔
2434
            throw std::runtime_error(util::format(
4✔
2435
                "A GEOWITHIN query can only operate on a link to an embedded class but '%1' is at the top level",
4✔
2436
                m_link_map.get_target_table()->get_class_name()));
4✔
2437
        }
4✔
2438
    }
324✔
2439

2440
    void set_cluster(const Cluster* cluster) override
2441
    {
340✔
2442
        m_link_map.set_cluster(cluster);
340✔
2443
    }
340✔
2444

2445
    void collect_dependencies(std::vector<TableKey>& tables) const override
2446
    {
28✔
2447
        m_link_map.collect_dependencies(tables);
28✔
2448
    }
28✔
2449

2450
    // Return main table of query (table on which table->where()... is invoked). Note that this is not the same as
2451
    // any linked-to payload tables
2452
    ConstTableRef get_base_table() const override
2453
    {
328✔
2454
        return m_link_map.get_base_table();
328✔
2455
    }
328✔
2456

2457
    size_t find_first(size_t start, size_t end) const override
2458
    {
896✔
2459
        auto table = m_link_map.get_target_table();
896✔
2460

448✔
2461
        while (start < end) {
2,664✔
2462
            bool found = false;
2,392✔
2463
            switch (m_comp_type.value_or(ExpressionComparisonType::Any)) {
2,392✔
2464
                case ExpressionComparisonType::Any: {
2,120✔
2465
                    m_link_map.map_links(start, [&](ObjKey key) {
2,156✔
2466
                        found = m_region.contains(
2,156✔
2467
                            Geospatial::point_from_obj(table->get_object(key), m_type_col, m_coords_col));
2,156✔
2468
                        return !found; // keep searching if not found, stop searching on first match
2,156✔
2469
                    });
2,156✔
2470
                    if (found)
2,120✔
2471
                        return start;
520✔
2472
                    break;
1,600✔
2473
                }
1,600✔
2474
                case ExpressionComparisonType::All: {
868✔
2475
                    m_link_map.map_links(start, [&](ObjKey key) {
128✔
2476
                        found = m_region.contains(
120✔
2477
                            Geospatial::point_from_obj(table->get_object(key), m_type_col, m_coords_col));
120✔
2478
                        return found; // keep searching until one the first non-match
120✔
2479
                    });
120✔
2480
                    if (found) // all matched
136✔
2481
                        return start;
4✔
2482
                    break;
132✔
2483
                }
132✔
2484
                case ExpressionComparisonType::None: {
136✔
2485
                    m_link_map.map_links(start, [&](ObjKey key) {
312✔
2486
                        found = m_region.contains(
312✔
2487
                            Geospatial::point_from_obj(table->get_object(key), m_type_col, m_coords_col));
312✔
2488
                        return !found; // keep searching until the first match
312✔
2489
                    });
312✔
2490
                    if (!found) // none matched
136✔
2491
                        return start;
100✔
2492
                    break;
36✔
2493
                }
36✔
2494
            }
1,768✔
2495
            start++;
1,768✔
2496
        }
1,768✔
2497

448✔
2498
        return not_found;
584✔
2499
    }
896✔
2500

2501
    std::string description(util::serializer::SerialisationState& state) const override
2502
    {
76✔
2503
        return state.describe_expression_type(m_comp_type) + state.describe_columns(m_link_map, ColKey()) +
76✔
2504
               " GEOWITHIN " + util::serializer::print_value(m_bounds);
76✔
2505
    }
76✔
2506

2507
    std::unique_ptr<Expression> clone() const override
2508
    {
184✔
2509
        return std::unique_ptr<Expression>(new GeoWithinCompare(*this));
184✔
2510
    }
184✔
2511

2512
private:
2513
    LinkMap m_link_map;
2514
    Geospatial m_bounds;
2515
    GeoRegion m_region;
2516
    ColKey m_type_col;
2517
    ColKey m_coords_col;
2518
    util::Optional<ExpressionComparisonType> m_comp_type;
2519
};
2520
#endif
2521

2522
template <class T, class TExpr>
2523
class SizeOperator : public Subexpr2<Int> {
2524
public:
2525
    SizeOperator(std::unique_ptr<TExpr> left)
2526
        : m_expr(std::move(left))
2527
    {
5,912✔
2528
    }
5,912✔
2529

2530
    SizeOperator(const SizeOperator& other)
2531
        : m_expr(other.m_expr->clone())
2532
    {
11,560✔
2533
    }
11,560✔
2534

2535
    // See comment in base class
2536
    void set_base_table(ConstTableRef table) override
2537
    {
6,212✔
2538
        m_expr->set_base_table(table);
6,212✔
2539
    }
6,212✔
2540

2541
    void set_cluster(const Cluster* cluster) override
2542
    {
6,608✔
2543
        m_expr->set_cluster(cluster);
6,608✔
2544
    }
6,608✔
2545

2546
    // Recursively fetch tables of columns in expression tree. Used when user first builds a stand-alone expression
2547
    // and binds it to a Query at a later time
2548
    ConstTableRef get_base_table() const override
2549
    {
5,912✔
2550
        return m_expr->get_base_table();
5,912✔
2551
    }
5,912✔
2552

2553
    // destination = operator(left)
2554
    void evaluate(size_t index, ValueBase& destination) override
2555
    {
72,383✔
2556
        Value<T> v;
72,383✔
2557
        m_expr->evaluate(index, v);
72,383✔
2558

36,187✔
2559
        size_t sz = v.size();
72,383✔
2560
        destination.init(v.m_from_list, sz);
72,383✔
2561

36,187✔
2562
        for (size_t i = 0; i < sz; i++) {
144,922✔
2563
            auto elem = v[i].template get<T>();
72,539✔
2564
            if constexpr (std::is_same_v<T, int64_t>) {
72,539✔
2565
                // This is the size of a list
16,959✔
2566
                destination.set(i, elem);
45,927✔
2567
            }
45,927✔
2568
            else {
33,921✔
2569
                if (!elem) {
33,921✔
2570
                    destination.set_null(i);
19,429✔
2571
                }
19,429✔
2572
                else {
33,801✔
2573
                    destination.set(i, int64_t(elem.size()));
33,801✔
2574
                }
33,801✔
2575
            }
33,921✔
2576
        }
72,539✔
2577
    }
72,383✔
2578

2579
    std::string description(util::serializer::SerialisationState& state) const override
2580
    {
2,272✔
2581
        if (m_expr) {
2,272✔
2582
            return m_expr->description(state) + util::serializer::value_separator + "@size";
2,272✔
2583
        }
2,272✔
2584
        return "@size";
×
2585
    }
×
2586

2587
    std::unique_ptr<Subexpr> clone() const override
2588
    {
11,560✔
2589
        return std::unique_ptr<Subexpr>(new SizeOperator(*this));
11,560✔
2590
    }
11,560✔
2591

2592
private:
2593
    std::unique_ptr<TExpr> m_expr;
2594
};
2595

2596
template <class T>
2597
class TypeOfValueOperator : public Subexpr2<TypeOfValue> {
2598
public:
2599
    TypeOfValueOperator(std::unique_ptr<Subexpr> left)
2600
        : m_expr(std::move(left))
2601
    {
608✔
2602
    }
608✔
2603

2604
    TypeOfValueOperator(const TypeOfValueOperator& other)
2605
        : m_expr(other.m_expr->clone())
2606
    {
1,200✔
2607
    }
1,200✔
2608

2609
    util::Optional<ExpressionComparisonType> get_comparison_type() const override
2610
    {
16,016✔
2611
        return m_expr->get_comparison_type();
16,016✔
2612
    }
16,016✔
2613

2614
    // See comment in base class
2615
    void set_base_table(ConstTableRef table) override
2616
    {
572✔
2617
        m_expr->set_base_table(table);
572✔
2618
    }
572✔
2619

2620
    void set_cluster(const Cluster* cluster) override
2621
    {
1,364✔
2622
        m_expr->set_cluster(cluster);
1,364✔
2623
    }
1,364✔
2624

2625
    // Recursively fetch tables of columns in expression tree. Used when user first builds a stand-alone expression
2626
    // and binds it to a Query at a later time
2627
    ConstTableRef get_base_table() const override
2628
    {
572✔
2629
        return m_expr->get_base_table();
572✔
2630
    }
572✔
2631

2632
    // destination = operator(left)
2633
    void evaluate(size_t index, ValueBase& destination) override
2634
    {
44,340✔
2635
        Value<T> v;
44,340✔
2636
        m_expr->evaluate(index, v);
44,340✔
2637

22,170✔
2638
        size_t sz = v.size();
44,340✔
2639
        destination.init(v.m_from_list, sz);
44,340✔
2640

22,170✔
2641
        for (size_t i = 0; i < sz; i++) {
90,764✔
2642
            auto elem = v[i].template get<T>();
46,424✔
2643
            destination.set(i, TypeOfValue(elem));
46,424✔
2644
        }
46,424✔
2645
    }
44,340✔
2646

2647
    std::string description(util::serializer::SerialisationState& state) const override
2648
    {
264✔
2649
        if (m_expr) {
264✔
2650
            return m_expr->description(state) + util::serializer::value_separator + "@type";
264✔
2651
        }
264✔
2652
        return "@type";
×
2653
    }
×
2654

2655
    std::unique_ptr<Subexpr> clone() const override
2656
    {
1,200✔
2657
        return std::unique_ptr<Subexpr>(new TypeOfValueOperator(*this));
1,200✔
2658
    }
1,200✔
2659

2660
private:
2661
    std::unique_ptr<Subexpr> m_expr;
2662
};
2663

2664
class KeyValue : public Subexpr2<Link> {
2665
public:
2666
    KeyValue(ObjKey key)
2667
        : m_key(key)
2668
    {
64✔
2669
    }
64✔
2670

2671
    void set_base_table(ConstTableRef) override {}
216✔
2672

2673
    ConstTableRef get_base_table() const override
2674
    {
64✔
2675
        return nullptr;
64✔
2676
    }
64✔
2677

2678
    void evaluate(size_t, ValueBase& destination) override
2679
    {
624✔
2680
        destination = Value<ObjKey>(m_key);
624✔
2681
    }
624✔
2682

2683
    std::string description(util::serializer::SerialisationState&) const override
2684
    {
4✔
2685
        return util::serializer::print_value(m_key);
4✔
2686
    }
4✔
2687

2688
    std::unique_ptr<Subexpr> clone() const override
2689
    {
256✔
2690
        return std::unique_ptr<Subexpr>(new KeyValue(*this));
256✔
2691
    }
256✔
2692

2693
private:
2694
    KeyValue(const KeyValue& source)
2695
        : m_key(source.m_key)
2696
    {
256✔
2697
    }
256✔
2698

2699
    ObjKey m_key;
2700
};
2701

2702
template <typename T>
2703
class SubColumns;
2704

2705
// This is for LinkList and BackLink too since they're declared as typedefs of Link.
2706
template <>
2707
class Columns<Link> : public Subexpr2<Link> {
2708
public:
2709
    Columns(const Columns& other)
2710
        : Subexpr2<Link>(other)
2711
        , m_link_map(other.m_link_map)
2712
        , m_comparison_type(other.m_comparison_type)
2713
        , m_is_list(other.m_is_list)
2714
    {
666✔
2715
    }
666✔
2716

2717
    Columns(ColKey column_key, ConstTableRef table, const std::vector<ExtendedColumnKey>& links = {},
2718
            util::Optional<ExpressionComparisonType> type = util::none)
2719
        : m_link_map(table, links)
2720
        , m_comparison_type(type)
2721
        , m_is_list(column_key.is_list())
2722
    {
5,936✔
2723
    }
5,936✔
2724

2725
    Query is_null()
2726
    {
76✔
2727
        if (m_link_map.get_nb_hops() > 1)
76✔
2728
            throw Exception(ErrorCodes::InvalidQuery, "Combining link() and is_null() is currently not supported");
2✔
2729
        // Todo, it may be useful to support the above, but we would need to figure out an intuitive behaviour
37✔
2730
        return make_expression<UnaryLinkCompare<false>>(m_link_map);
74✔
2731
    }
74✔
2732

2733
    Query is_not_null()
2734
    {
52✔
2735
        if (m_link_map.get_nb_hops() > 1)
52✔
2736
            throw Exception(ErrorCodes::InvalidQuery,
×
2737
                            "Combining link() and is_not_null() is currently not supported");
×
2738
        // Todo, it may be useful to support the above, but we would need to figure out an intuitive behaviour
26✔
2739
        return make_expression<UnaryLinkCompare<true>>(m_link_map);
52✔
2740
    }
52✔
2741

2742
#if REALM_ENABLE_GEOSPATIAL
2743
    Query geo_within(Geospatial bounds) const
2744
    {
360✔
2745
        return make_expression<GeoWithinCompare>(m_link_map, std::move(bounds), m_comparison_type);
360✔
2746
    }
360✔
2747
#endif
2748

2749
    LinkCount count() const
2750
    {
852✔
2751
        return LinkCount(m_link_map);
852✔
2752
    }
852✔
2753

2754
    template <class T>
2755
    BacklinkCount<T> backlink_count() const
2756
    {
2757
        return BacklinkCount<T>(m_link_map);
2758
    }
2759

2760
    template <typename C>
2761
    SubColumns<C> column(ColKey column_key) const
2762
    {
2,964✔
2763
        // no need to pass along m_comparison_type because the only operations supported from
1,482✔
2764
        // the subsequent SubColumns are aggregate operations such as sum, min, max, avg where
1,482✔
2765
        // having
1,482✔
2766
        REALM_ASSERT_DEBUG(!m_comparison_type);
2,964✔
2767
        return SubColumns<C>(Columns<C>(column_key, m_link_map.get_target_table()), m_link_map);
2,964✔
2768
    }
2,964✔
2769

2770
    const LinkMap& link_map() const
2771
    {
3,108✔
2772
        return m_link_map;
3,108✔
2773
    }
3,108✔
2774

2775
    DataType get_type() const override
2776
    {
1,452✔
2777
        return type_Link;
1,452✔
2778
    }
1,452✔
2779

2780
    ConstTableRef get_target_table() const override
2781
    {
96✔
2782
        return link_map().get_target_table();
96✔
2783
    }
96✔
2784

2785
    bool has_multiple_values() const override
2786
    {
618✔
2787
        return m_is_list || !m_link_map.only_unary_links();
618✔
2788
    }
618✔
2789

2790
    ConstTableRef get_base_table() const override
2791
    {
1,092✔
2792
        return m_link_map.get_base_table();
1,092✔
2793
    }
1,092✔
2794

2795
    void set_base_table(ConstTableRef table) override
2796
    {
504✔
2797
        m_link_map.set_base_table(table);
504✔
2798
    }
504✔
2799

2800
    void set_cluster(const Cluster* cluster) override
2801
    {
318✔
2802
        REALM_ASSERT(m_link_map.has_links());
318✔
2803
        m_link_map.set_cluster(cluster);
318✔
2804
    }
318✔
2805

2806
    void collect_dependencies(std::vector<TableKey>& tables) const override
2807
    {
276✔
2808
        m_link_map.collect_dependencies(tables);
276✔
2809
    }
276✔
2810

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

2816
    util::Optional<ExpressionComparisonType> get_comparison_type() const override
2817
    {
1,584✔
2818
        return m_comparison_type;
1,584✔
2819
    }
1,584✔
2820

2821
    std::unique_ptr<Subexpr> clone() const override
2822
    {
666✔
2823
        return std::unique_ptr<Subexpr>(new Columns<Link>(*this));
666✔
2824
    }
666✔
2825

2826
    void evaluate(size_t index, ValueBase& destination) override;
2827

2828
private:
2829
    LinkMap m_link_map;
2830
    util::Optional<ExpressionComparisonType> m_comparison_type;
2831
    bool m_is_list;
2832
    friend class Table;
2833
    friend class LinkChain;
2834
};
2835

2836
template <typename T>
2837
class ListColumns;
2838
template <typename T, typename Operation>
2839
class CollectionColumnAggregate;
2840
namespace aggregate_operations {
2841
template <typename T>
2842
class Minimum;
2843
template <typename T>
2844
class Maximum;
2845
template <typename T>
2846
class Sum;
2847
template <typename T>
2848
class Average;
2849
} // namespace aggregate_operations
2850

2851
class ColumnListBase {
2852
public:
2853
    ColumnListBase(ColKey column_key, ConstTableRef table, const std::vector<ExtendedColumnKey>& links,
2854
                   util::Optional<ExpressionComparisonType> type = util::none)
2855
        : m_column_key(column_key)
2856
        , m_link_map(table, links)
2857
        , m_comparison_type(type)
2858
    {
24,232✔
2859
    }
24,232✔
2860

2861
    ColumnListBase(const ColumnListBase& other)
2862
        : m_column_key(other.m_column_key)
2863
        , m_link_map(other.m_link_map)
2864
        , m_comparison_type(other.m_comparison_type)
2865
    {
72,009✔
2866
    }
72,009✔
2867

2868
    void set_cluster(const Cluster* cluster);
2869

2870
    void get_lists(size_t index, Value<int64_t>& destination, size_t nb_elements);
2871

2872
    std::string description(util::serializer::SerialisationState& state) const
2873
    {
12,960✔
2874
        return state.describe_expression_type(m_comparison_type) + state.describe_columns(m_link_map, m_column_key);
12,960✔
2875
    }
12,960✔
2876

2877
    bool links_exist() const
2878
    {
38,250✔
2879
        return m_link_map.has_links();
38,250✔
2880
    }
38,250✔
2881

2882
    virtual SizeOperator<int64_t> size() = 0;
2883
    virtual std::unique_ptr<Subexpr> get_element_length() = 0;
2884
    virtual std::unique_ptr<Subexpr> max_of() = 0;
2885
    virtual std::unique_ptr<Subexpr> min_of() = 0;
2886
    virtual std::unique_ptr<Subexpr> sum_of() = 0;
2887
    virtual std::unique_ptr<Subexpr> avg_of() = 0;
2888

2889
    virtual bool index(const PathElement&) = 0;
2890

2891
    mutable ColKey m_column_key;
2892
    LinkMap m_link_map;
2893
    std::optional<ArrayInteger> m_leaf;
2894
    std::optional<ExpressionComparisonType> m_comparison_type;
2895
};
2896

2897
template <typename>
2898
class ColumnListSize;
2899

2900
template <typename T>
2901
class ColumnListElementLength;
2902

2903
template <typename T>
2904
class ColumnsCollection : public Subexpr2<T>, public ColumnListBase {
2905
public:
2906
    ColumnsCollection(ColKey column_key, ConstTableRef table, const std::vector<ExtendedColumnKey>& links = {},
2907
                      util::Optional<ExpressionComparisonType> type = util::none)
2908
        : ColumnListBase(column_key, table, links, type)
2909
        , m_is_nullable_storage(this->m_column_key.get_attrs().test(col_attr_Nullable))
2910
    {
24,216✔
2911
    }
24,216✔
2912

2913
    ColumnsCollection(const ColumnsCollection& other)
2914
        : Subexpr2<T>(other)
2915
        , ColumnListBase(other)
2916
        , m_is_nullable_storage(this->m_column_key.get_attrs().test(col_attr_Nullable))
2917
        , m_index(other.m_index)
2918
    {
51,508✔
2919
    }
51,508✔
2920

2921
    bool has_multiple_values() const override
2922
    {
164✔
2923
        return true;
164✔
2924
    }
164✔
2925

2926
    ConstTableRef get_base_table() const final
2927
    {
59,646✔
2928
        return m_link_map.get_base_table();
59,646✔
2929
    }
59,646✔
2930
    ConstTableRef get_target_table() const override
2931
    {
4,680✔
2932
        return m_link_map.get_target_table()->get_opposite_table(m_column_key);
4,680✔
2933
    }
4,680✔
2934

2935
    Allocator& get_alloc() const
2936
    {
1,358,756✔
2937
        return m_link_map.get_target_table()->get_alloc();
1,358,756✔
2938
    }
1,358,756✔
2939

2940
    void set_base_table(ConstTableRef table) final
2941
    {
23,784✔
2942
        m_link_map.set_base_table(table);
23,784✔
2943
    }
23,784✔
2944

2945
    void set_cluster(const Cluster* cluster) final
2946
    {
28,680✔
2947
        ColumnListBase::set_cluster(cluster);
28,680✔
2948
    }
28,680✔
2949

2950
    void collect_dependencies(std::vector<TableKey>& tables) const final
2951
    {
712✔
2952
        m_link_map.collect_dependencies(tables);
712✔
2953
    }
712✔
2954

2955
    void evaluate(size_t index, ValueBase& destination) override
2956
    {
89,608✔
2957
        if constexpr (realm::is_any_v<T, ObjectId, Int, Bool, UUID>) {
89,608✔
2958
            if (m_is_nullable_storage) {
43,312✔
2959
                evaluate<util::Optional<T>>(index, destination);
9,080✔
2960
                return;
9,080✔
2961
            }
9,080✔
2962
        }
34,608✔
2963
        evaluate<T>(index, destination);
34,608✔
2964
    }
34,608✔
2965

2966
    std::string description(util::serializer::SerialisationState& state) const override
2967
    {
8,832✔
2968
        std::string index_string;
8,832✔
2969
        if (m_index) {
8,832!
2970
            PathElement p(*m_index);
584✔
2971
            index_string = "[" + util::to_string(p) + "]";
584✔
2972
        }
584✔
2973
        return ColumnListBase::description(state) + index_string;
8,832✔
2974
    }
8,832✔
2975

2976
    util::Optional<ExpressionComparisonType> get_comparison_type() const final
2977
    {
62,408✔
2978
        return ColumnListBase::m_comparison_type;
62,408✔
2979
    }
62,408✔
2980

2981
    SizeOperator<int64_t> size() override;
2982

2983
    ColumnListElementLength<T> element_lengths() const
2984
    {
1,636✔
2985
        return {*this};
1,636✔
2986
    }
1,636✔
2987

2988
    TypeOfValueOperator<T> type_of_value()
2989
    {
128✔
2990
        return TypeOfValueOperator<T>(this->clone());
128✔
2991
    }
128✔
2992

2993
    CollectionColumnAggregate<T, aggregate_operations::Minimum<T>> min() const
2994
    {
1,420✔
2995
        return {*this};
1,420✔
2996
    }
1,420✔
2997

2998
    CollectionColumnAggregate<T, aggregate_operations::Maximum<T>> max() const
2999
    {
1,460✔
3000
        return {*this};
1,460✔
3001
    }
1,460✔
3002

3003
    CollectionColumnAggregate<T, aggregate_operations::Sum<T>> sum() const
3004
    {
1,444✔
3005
        return {*this};
1,444✔
3006
    }
1,444✔
3007

3008
    CollectionColumnAggregate<T, aggregate_operations::Average<T>> average() const
3009
    {
1,256✔
3010
        return {*this};
1,256✔
3011
    }
1,256✔
3012

3013
    std::unique_ptr<Subexpr> max_of() override
3014
    {
792✔
3015
        if constexpr (realm::is_any_v<T, Int, Float, Double, Decimal128, Mixed>) {
792✔
3016
            return max().clone();
208✔
3017
        }
208✔
3018
        else {
208✔
3019
            return {};
208✔
3020
        }
208✔
3021
    }
792✔
3022
    std::unique_ptr<Subexpr> min_of() override
3023
    {
752✔
3024
        if constexpr (realm::is_any_v<T, Int, Float, Double, Decimal128, Mixed>) {
752✔
3025
            return min().clone();
192✔
3026
        }
192✔
3027
        else {
192✔
3028
            return {};
192✔
3029
        }
192✔
3030
    }
752✔
3031
    std::unique_ptr<Subexpr> sum_of() override
3032
    {
980✔
3033
        if constexpr (realm::is_any_v<T, Int, Float, Double, Decimal128, Mixed>) {
980✔
3034
            return sum().clone();
228✔
3035
        }
228✔
3036
        else {
228✔
3037
            return {};
228✔
3038
        }
228✔
3039
    }
980✔
3040
    std::unique_ptr<Subexpr> avg_of() override
3041
    {
820✔
3042
        if constexpr (realm::is_any_v<T, Int, Float, Double, Decimal128, Mixed>) {
820✔
3043
            return average().clone();
196✔
3044
        }
196✔
3045
        else {
196✔
3046
            return {};
196✔
3047
        }
196✔
3048
    }
820✔
3049

3050
    std::unique_ptr<Subexpr> get_element_length() override
3051
    {
1,700✔
3052
        if constexpr (realm::is_any_v<T, StringData, BinaryData, Mixed>) {
1,700✔
3053
            return element_lengths().clone();
76✔
3054
        }
76✔
3055
        else {
76✔
3056
            return {};
76✔
3057
        }
76✔
3058
    }
1,700✔
3059

3060
    std::unique_ptr<Subexpr> clone() const override
3061
    {
×
3062
        return std::unique_ptr<Subexpr>(new ColumnsCollection(*this));
×
3063
    }
×
3064

3065
    bool index(const PathElement& ndx) override
3066
    {
3,002✔
3067
        if (ndx.is_ndx()) {
3,002!
3068
            m_index = ndx.get_ndx();
1,210✔
3069
            return true;
1,210✔
3070
        }
1,210✔
3071
        else if (ndx.is_all()) {
1,792!
3072
            return true;
204✔
3073
        }
204✔
3074
        return false;
1,588✔
3075
    }
1,588✔
3076
    const bool m_is_nullable_storage;
3077
    std::optional<size_t> m_index;
3078

3079
protected:
3080
    template <typename StorageType>
3081
    void evaluate(size_t index, ValueBase& destination)
3082
    {
99,946✔
3083
        Allocator& alloc = get_alloc();
99,946✔
3084
        Value<int64_t> list_refs;
99,946✔
3085
        get_lists(index, list_refs, 1);
99,946✔
3086
        const bool is_from_list = true;
99,946✔
3087

49,973✔
3088
        std::vector<StorageType> values;
99,946✔
3089
        for (auto&& i : list_refs) {
101,278!
3090
            ref_type list_ref = to_ref(i.get_int());
101,278✔
3091
            if (list_ref) {
101,278!
3092
                BPlusTree<StorageType> list(alloc);
73,690✔
3093
                list.init_from_ref(list_ref);
73,690✔
3094
                if (size_t s = list.size()) {
73,690!
3095
                    if (m_index) {
73,690✔
3096
                        if (*m_index < s) {
4,740!
3097
                            values.push_back(list.get(*m_index));
2,028✔
3098
                        }
2,028✔
3099
                        else if (*m_index == size_t(-1)) {
2,712!
3100
                            values.push_back(list.get(s - 1));
1,560✔
3101
                        }
1,560✔
3102
                    }
4,740✔
3103
                    else {
68,950✔
3104
                        for (size_t j = 0; j < s; j++) {
212,286!
3105
                            values.push_back(list.get(j));
143,336✔
3106
                        }
143,336✔
3107
                    }
68,950✔
3108
                }
73,690✔
3109
            }
73,690✔
3110
        }
101,278✔
3111
        destination.init(is_from_list, values.size());
99,946✔
3112
        destination.set(values.begin(), values.end());
99,946✔
3113
    }
99,946✔
3114
};
3115

3116
template <typename T>
3117
class Columns<Lst<T>> : public ColumnsCollection<T> {
3118
public:
3119
    using ColumnsCollection<T>::ColumnsCollection;
3120
    std::unique_ptr<Subexpr> clone() const override
3121
    {
10,216✔
3122
        return make_subexpr<Columns<Lst<T>>>(*this);
10,216✔
3123
    }
10,216✔
3124
    friend class Table;
3125
    friend class LinkChain;
3126
};
3127

3128
template <typename T>
3129
class Columns<Set<T>> : public ColumnsCollection<T> {
3130
public:
3131
    using ColumnsCollection<T>::ColumnsCollection;
3132
    std::unique_ptr<Subexpr> clone() const override
3133
    {
1,792✔
3134
        return make_subexpr<Columns<Set<T>>>(*this);
1,792✔
3135
    }
1,792✔
3136
    bool index(const PathElement&) override
3137
    {
144✔
3138
        return false;
144✔
3139
    }
144✔
3140
};
3141

3142

3143
template <>
3144
class Columns<LnkLst> : public Columns<Lst<ObjKey>> {
3145
public:
3146
    using Columns<Lst<ObjKey>>::Columns;
3147

3148
    std::unique_ptr<Subexpr> clone() const override
3149
    {
×
3150
        return make_subexpr<Columns<LnkLst>>(*this);
×
3151
    }
×
3152
};
3153

3154
template <>
3155
class Columns<LnkSet> : public Columns<Set<ObjKey>> {
3156
public:
3157
    using Columns<Set<ObjKey>>::Columns;
3158

3159
    std::unique_ptr<Subexpr> clone() const override
3160
    {
×
3161
        return make_subexpr<Columns<LnkSet>>(*this);
×
3162
    }
×
3163
};
3164

3165
template <>
3166
class Columns<Lst<Mixed>> : public ColumnsCollection<Mixed> {
3167
public:
3168
    using ColumnsCollection<Mixed>::ColumnsCollection;
3169
    std::unique_ptr<Subexpr> clone() const override
3170
    {
456✔
3171
        return make_subexpr<Columns<Lst<Mixed>>>(*this);
456✔
3172
    }
456✔
3173
    friend class Table;
3174
    friend class LinkChain;
3175
    bool indexes(const Path& path)
3176
    {
44✔
3177
        REALM_ASSERT(!path.empty());
44✔
3178
        if (ColumnsCollection<Mixed>::index(path[0])) {
44✔
3179
            for (auto& elem : path) {
72✔
3180
                m_path.emplace_back(elem);
72✔
3181
            }
72✔
3182
            return true;
36✔
3183
        }
36✔
3184
        return false;
8✔
3185
    }
8✔
3186
    Columns<Lst<Mixed>>& path(const Path& path)
3187
    {
2✔
3188
        if (!indexes(path)) {
2✔
3189
            throw InvalidArgument("Illegal path");
×
3190
        }
×
3191
        return *this;
2✔
3192
    }
2✔
3193
    std::string description(util::serializer::SerialisationState& state) const override
3194
    {
112✔
3195
        return ColumnListBase::description(state) + util::to_string(m_path);
112✔
3196
    }
112✔
3197

3198
    void evaluate(size_t index, ValueBase& destination) override
3199
    {
6,892✔
3200
        // Base class will handle path[0] and return result in destination
3,446✔
3201
        ColumnsCollection<Mixed>::evaluate<Mixed>(index, destination);
6,892✔
3202
        if (m_path.size() > 1) {
6,892✔
3203
            if (auto sz = destination.size()) {
72✔
3204
                Collection::QueryCtrlBlock ctrl(m_path, *get_base_table(), destination.m_from_list);
72✔
3205
                for (size_t i = 0; i < sz; i++) {
144✔
3206
                    Collection::get_any(ctrl, destination.get(i), 1);
72✔
3207
                }
72✔
3208
                destination.init(ctrl.from_list, ctrl.matches.size());
72✔
3209
                destination.set(ctrl.matches.begin(), ctrl.matches.end());
72✔
3210
            }
72✔
3211
        }
72✔
3212
    }
6,892✔
3213

3214
private:
3215
    Path m_path;
3216
};
3217

3218
// Returns the keys
3219
class ColumnDictionaryKeys;
3220

3221
// Returns the values
3222
template <>
3223
class Columns<Dictionary> : public ColumnsCollection<Mixed> {
3224
public:
3225
    Columns(ColKey column, ConstTableRef table, const std::vector<ExtendedColumnKey>& links = {},
3226
            util::Optional<ExpressionComparisonType> type = util::none)
3227
        : ColumnsCollection<Mixed>(column, table, links, type)
3228
    {
1,424✔
3229
        m_key_type = m_link_map.get_target_table()->get_dictionary_key_type(m_column_key);
1,424✔
3230
        m_path.push_back(PathElement::AllTag());
1,424✔
3231
        m_path_only_unary_keys = false;
1,424✔
3232
    }
1,424✔
3233

3234
    Columns(const Path& path, ConstTableRef table, const std::vector<ExtendedColumnKey>& links = {},
3235
            util::Optional<ExpressionComparisonType> type = util::none)
3236
        : ColumnsCollection<Mixed>(path[0].get_col_key(), table, links, type)
3237
    {
×
3238
        size_t path_size = path.size();
×
3239
        REALM_ASSERT(path_size > 0);
×
3240
        if (path_size == 1) {
×
3241
            m_key_type = m_link_map.get_target_table()->get_dictionary_key_type(m_column_key);
×
3242
        }
×
3243
        init_path(&path[1], &path[1] + path_size - 1);
×
3244
    }
×
3245

3246
    // Change the node to handle a specific key value only
3247
    Columns<Dictionary>& key(StringData key)
3248
    {
208✔
3249
        PathElement p(key);
208✔
3250
        init_path(&p, &p + 1);
208✔
3251
        return *this;
208✔
3252
    }
208✔
3253

3254
    // Change the node to handle a specific key value only
3255
    Columns<Dictionary>& path(const Path& path)
3256
    {
172✔
3257
        auto sz = path.size();
172✔
3258
        const PathElement* first = &path[0];
172✔
3259
        init_path(first, first + sz);
172✔
3260
        return *this;
172✔
3261
    }
172✔
3262

3263
    DataType get_key_type() const
3264
    {
78✔
3265
        return m_key_type;
78✔
3266
    }
78✔
3267

3268
    ColumnDictionaryKeys keys();
3269

3270
    SizeOperator<int64_t> size() override;
3271
    std::unique_ptr<Subexpr> get_element_length() override
3272
    {
×
3273
        // Not supported for Dictionary
3274
        return {};
×
3275
    }
×
3276

3277
    void evaluate(size_t index, ValueBase& destination) override;
3278

3279
    std::string description(util::serializer::SerialisationState& state) const override
3280
    {
138✔
3281
        return ColumnListBase::description(state) + util::to_string(m_path);
138✔
3282
    }
138✔
3283

3284
    std::unique_ptr<Subexpr> clone() const override
3285
    {
1,086✔
3286
        return make_subexpr<Columns<Dictionary>>(*this);
1,086✔
3287
    }
1,086✔
3288

3289
    bool index(const PathElement&) override
3290
    {
×
3291
        return false;
×
3292
    }
×
3293

3294
    Columns(Columns const& other)
3295
        : ColumnsCollection<Mixed>(other)
3296
        , m_key_type(other.m_key_type)
3297
        , m_path(other.m_path)
3298
        , m_path_only_unary_keys(other.m_path_only_unary_keys)
3299
    {
1,122✔
3300
    }
1,122✔
3301

3302
protected:
3303
    DataType m_key_type = type_String;
3304
    Path m_path;
3305
    bool m_path_only_unary_keys;
3306

3307
    void init_path(const PathElement* begin, const PathElement* end);
3308
};
3309

3310
// Returns the keys
3311
class ColumnDictionaryKeys : public Subexpr2<StringData> {
3312
public:
3313
    ColumnDictionaryKeys(const Columns<Dictionary>& dict)
3314
        : m_key_type(dict.get_key_type())
3315
        , m_column_key(dict.m_column_key)
3316
        , m_link_map(dict.m_link_map)
3317
        , m_comparison_type(dict.get_comparison_type())
3318
    {
78✔
3319
        REALM_ASSERT(m_key_type == type_String);
78✔
3320
    }
78✔
3321

3322
    ConstTableRef get_base_table() const final
3323
    {
6,684✔
3324
        return m_link_map.get_base_table();
6,684✔
3325
    }
6,684✔
3326

3327
    void set_base_table(ConstTableRef table) final
3328
    {
78✔
3329
        m_link_map.set_base_table(table);
78✔
3330
    }
78✔
3331

3332
    void collect_dependencies(std::vector<TableKey>& tables) const final
3333
    {
12✔
3334
        m_link_map.collect_dependencies(tables);
12✔
3335
    }
12✔
3336

3337
    util::Optional<ExpressionComparisonType> get_comparison_type() const final
3338
    {
2,070✔
3339
        return m_comparison_type;
2,070✔
3340
    }
2,070✔
3341

3342
    void set_cluster(const Cluster* cluster) override;
3343
    void evaluate(size_t index, ValueBase& destination) override;
3344

3345
    std::string description(util::serializer::SerialisationState& state) const override
3346
    {
30✔
3347
        return state.describe_expression_type(m_comparison_type) + state.describe_columns(m_link_map, m_column_key) +
30✔
3348
               ".@keys";
30✔
3349
    }
30✔
3350

3351
    std::unique_ptr<Subexpr> clone() const override
3352
    {
90✔
3353
        return std::unique_ptr<Subexpr>(new ColumnDictionaryKeys(*this));
90✔
3354
    }
90✔
3355

3356
    ColumnDictionaryKeys(const ColumnDictionaryKeys& other)
3357
        : m_key_type(other.m_key_type)
3358
        , m_column_key(other.m_column_key)
3359
        , m_link_map(other.m_link_map)
3360
        , m_comparison_type(other.m_comparison_type)
3361
    {
90✔
3362
    }
90✔
3363

3364
private:
3365
    DataType m_key_type;
3366
    ColKey m_column_key;
3367
    LinkMap m_link_map;
3368
    std::optional<ExpressionComparisonType> m_comparison_type;
3369
    std::optional<ArrayInteger> m_leaf;
3370
};
3371

3372
template <typename T>
3373
class ColumnListSize : public ColumnsCollection<T> {
3374
public:
3375
    ColumnListSize(const ColumnsCollection<T>& other)
3376
        : ColumnsCollection<T>(other)
3377
    {
3,540✔
3378
    }
3,540✔
3379
    void evaluate(size_t index, ValueBase& destination) override
3380
    {
40,058✔
3381
        if constexpr (realm::is_any_v<T, ObjectId, Int, Bool, UUID>) {
40,058✔
3382
            if (this->m_is_nullable_storage) {
22,898✔
3383
                evaluate<util::Optional<T>>(index, destination);
5,406✔
3384
                return;
5,406✔
3385
            }
5,406✔
3386
        }
19,850✔
3387
        evaluate<T>(index, destination);
19,850✔
3388
    }
19,850✔
3389

3390
    std::unique_ptr<Subexpr> clone() const override
3391
    {
6,950✔
3392
        return std::unique_ptr<Subexpr>(new ColumnListSize<T>(*this));
6,950✔
3393
    }
6,950✔
3394

3395
private:
3396
    template <typename StorageType>
3397
    void evaluate(size_t index, ValueBase& destination)
3398
    {
40,058✔
3399
        Allocator& alloc = ColumnsCollection<T>::get_alloc();
40,058✔
3400
        Value<int64_t> list_refs;
40,058✔
3401
        this->get_lists(index, list_refs, 1);
40,058✔
3402
        destination.init(list_refs.m_from_list, list_refs.size());
40,058✔
3403
        for (size_t i = 0; i < list_refs.size(); i++) {
80,220✔
3404
            ref_type list_ref = to_ref(list_refs[i].get_int());
40,162✔
3405
            if (list_ref) {
40,162✔
3406
                BPlusTree<StorageType> list(alloc);
23,940✔
3407
                list.init_from_ref(list_ref);
23,940✔
3408
                size_t s = list.size();
23,940✔
3409
                destination.set(i, int64_t(s));
23,940✔
3410
            }
23,940✔
3411
            else {
16,222✔
3412
                destination.set(i, 0);
16,222✔
3413
            }
16,222✔
3414
        }
40,162✔
3415
    }
40,058✔
3416
};
3417

3418

3419
template <typename T>
3420
class ColumnListElementLength : public Subexpr2<Int> {
3421
public:
3422
    ColumnListElementLength(const ColumnsCollection<T>& source)
3423
        : m_list(source)
3424
    {
1,636✔
3425
    }
1,636✔
3426
    bool has_multiple_values() const override
3427
    {
120✔
3428
        return true;
120✔
3429
    }
120✔
3430
    void evaluate(size_t index, ValueBase& destination) override
3431
    {
9,820✔
3432
        Allocator& alloc = m_list.get_alloc();
9,820✔
3433
        Value<int64_t> list_refs;
9,820✔
3434
        m_list.get_lists(index, list_refs, 1);
9,820✔
3435
        std::vector<Int> sizes;
9,820✔
3436
        for (size_t i = 0; i < list_refs.size(); i++) {
19,640✔
3437
            ref_type list_ref = to_ref(list_refs[i].get_int());
9,820✔
3438
            if (list_ref) {
9,820✔
3439
                BPlusTree<T> list(alloc);
8,184✔
3440
                list.init_from_ref(list_ref);
8,184✔
3441
                const size_t list_size = list.size();
8,184✔
3442
                sizes.reserve(sizes.size() + list_size);
8,184✔
3443
                for (size_t j = 0; j < list_size; j++) {
19,176✔
3444
                    if constexpr (std::is_same_v<T, Mixed>) {
10,992✔
3445
                        Mixed v = list.get(j);
10,512✔
3446
                        if (!v.is_null()) {
5,616✔
3447
                            if (v.get_type() == type_String) {
600✔
3448
                                sizes.push_back(v.get_string().size());
120✔
3449
                            }
120✔
3450
                            else if (v.get_type() == type_Binary) {
480✔
3451
                                sizes.push_back(v.get_binary().size());
24✔
3452
                            }
24✔
3453
                        }
600✔
3454
                    }
720✔
3455
                    else {
10,512✔
3456
                        sizes.push_back(list.get(j).size());
10,512✔
3457
                    }
10,512✔
3458
                }
10,992✔
3459
            }
8,184✔
3460
        }
9,820✔
3461
        constexpr bool is_from_list = true;
9,820✔
3462
        destination.init(is_from_list, sizes.size());
9,820✔
3463
        destination.set(sizes.begin(), sizes.end());
9,820✔
3464
    }
9,820✔
3465
    ConstTableRef get_base_table() const override
3466
    {
1,636✔
3467
        return m_list.get_base_table();
1,636✔
3468
    }
1,636✔
3469

3470
    void set_base_table(ConstTableRef table) override
3471
    {
1,636✔
3472
        m_list.set_base_table(table);
1,636✔
3473
    }
1,636✔
3474

3475
    void set_cluster(const Cluster* cluster) override
3476
    {
1,636✔
3477
        m_list.set_cluster(cluster);
1,636✔
3478
    }
1,636✔
3479

3480
    void collect_dependencies(std::vector<TableKey>& tables) const override
3481
    {
×
3482
        m_list.collect_dependencies(tables);
×
3483
    }
×
3484

3485
    std::unique_ptr<Subexpr> clone() const override
3486
    {
3,272✔
3487
        return std::unique_ptr<Subexpr>(new ColumnListElementLength<T>(*this));
3,272✔
3488
    }
3,272✔
3489

3490
    std::string description(util::serializer::SerialisationState& state) const override
3491
    {
818✔
3492
        return m_list.description(state) + util::serializer::value_separator + "length";
818✔
3493
    }
818✔
3494

3495
    util::Optional<ExpressionComparisonType> get_comparison_type() const override
3496
    {
5,196✔
3497
        return m_list.get_comparison_type();
5,196✔
3498
    }
5,196✔
3499

3500
private:
3501
    ColumnsCollection<T> m_list;
3502
};
3503

3504

3505
template <typename T>
3506
SizeOperator<int64_t> ColumnsCollection<T>::size()
3507
{
3,540✔
3508
    std::unique_ptr<Subexpr> ptr(new ColumnListSize<T>(*this));
3,540✔
3509
    return SizeOperator<int64_t>(std::move(ptr));
3,540✔
3510
}
3,540✔
3511

3512
template <typename T, typename Operation>
3513
class CollectionColumnAggregate : public Subexpr2<decltype(Operation().result())> {
3514
public:
3515
    CollectionColumnAggregate(ColumnsCollection<T> column)
3516
        : m_columns_collection(std::move(column))
3517
    {
5,580✔
3518
        if (m_columns_collection.m_column_key.is_dictionary()) {
5,580✔
3519
            m_dictionary_key_type = m_columns_collection.m_link_map.get_target_table()->get_dictionary_key_type(
1,104✔
3520
                m_columns_collection.m_column_key);
1,104✔
3521
        }
1,104✔
3522
    }
5,580✔
3523

3524
    CollectionColumnAggregate(const CollectionColumnAggregate& other)
3525
        : m_columns_collection(other.m_columns_collection)
3526
        , m_dictionary_key_type(other.m_dictionary_key_type)
3527
    {
10,868✔
3528
    }
10,868✔
3529

3530
    DataType get_type() const override
3531
    {
5,952✔
3532
        return DataType(m_columns_collection.m_column_key.get_type());
5,952✔
3533
    }
5,952✔
3534

3535
    std::unique_ptr<Subexpr> clone() const override
3536
    {
10,868✔
3537
        return make_subexpr<CollectionColumnAggregate>(*this);
10,868✔
3538
    }
10,868✔
3539

3540
    ConstTableRef get_base_table() const override
3541
    {
5,580✔
3542
        return m_columns_collection.get_base_table();
5,580✔
3543
    }
5,580✔
3544

3545
    void set_base_table(ConstTableRef table) override
3546
    {
6,060✔
3547
        m_columns_collection.set_base_table(table);
6,060✔
3548
    }
6,060✔
3549

3550
    void set_cluster(const Cluster* cluster) override
3551
    {
10,396✔
3552
        m_columns_collection.set_cluster(cluster);
10,396✔
3553
    }
10,396✔
3554

3555
    void collect_dependencies(std::vector<TableKey>& tables) const override
3556
    {
488✔
3557
        m_columns_collection.collect_dependencies(tables);
488✔
3558
    }
488✔
3559

3560
    void evaluate(size_t index, ValueBase& destination) override
3561
    {
1,132,552✔
3562
        if (m_dictionary_key_type) {
1,132,552✔
3563
            if (m_columns_collection.links_exist()) {
17,508!
3564
                std::vector<ObjKey> links = m_columns_collection.m_link_map.get_links(index);
1,212✔
3565
                auto sz = links.size();
1,212✔
3566

606✔
3567
                destination.init_for_links(m_columns_collection.m_link_map.only_unary_links(), sz);
1,212✔
3568
                if (sz == 0 && m_columns_collection.m_link_map.only_unary_links()) {
1,212!
3569
                    set_value_for_empty_dictionary(destination, 0);
324✔
3570
                }
324✔
3571
                for (size_t t = 0; t < sz; t++) {
3,180!
3572
                    const Obj obj = m_columns_collection.m_link_map.get_target_table()->get_object(links[t]);
1,968✔
3573
                    auto dict = obj.get_dictionary(m_columns_collection.m_column_key);
1,968✔
3574
                    if (dict.size() > 0) {
1,968!
3575
                        destination.set(t, do_dictionary_agg(dict));
1,644✔
3576
                    }
1,644✔
3577
                    else {
324✔
3578
                        set_value_for_empty_dictionary(destination, t);
324✔
3579
                    }
324✔
3580
                }
1,968✔
3581
            }
1,212✔
3582
            else {
16,296✔
3583
                if (auto ref = m_columns_collection.m_leaf->get(index)) {
16,296!
3584
                    Allocator& alloc = m_columns_collection.get_base_table()->get_alloc();
15,732✔
3585
                    Dictionary dict(alloc, m_columns_collection.m_column_key, to_ref(ref));
15,732✔
3586
                    destination.set(0, do_dictionary_agg(dict));
15,732✔
3587
                }
15,732✔
3588
                else {
564✔
3589
                    set_value_for_empty_dictionary(destination, 0);
564✔
3590
                }
564✔
3591
            }
16,296✔
3592
        }
17,508✔
3593
        else {
1,115,044✔
3594
            Allocator& alloc = m_columns_collection.get_alloc();
1,115,044✔
3595
            Value<int64_t> list_refs;
1,115,044✔
3596
            m_columns_collection.get_lists(index, list_refs, 1);
1,115,044✔
3597
            size_t sz = list_refs.size();
1,115,044✔
3598
            REALM_ASSERT_DEBUG(sz > 0 || list_refs.m_from_list);
1,115,044!
3599
            // The result is an aggregate value for each table
557,522✔
3600
            destination.init_for_links(!list_refs.m_from_list, sz);
1,115,044✔
3601
            for (size_t i = 0; i < list_refs.size(); i++) {
2,230,432✔
3602
                auto list_ref = to_ref(list_refs[i].get_int());
1,115,388✔
3603
                Operation op;
1,115,388✔
3604
                if (list_ref) {
1,115,388✔
3605
                    if constexpr (realm::is_any_v<T, ObjectId, Int, Bool, UUID>) {
1,086,348✔
3606
                        if (m_columns_collection.m_is_nullable_storage) {
543,818✔
3607
                            accumulate<util::Optional<T>>(op, alloc, list_ref);
190,120✔
3608
                        }
190,120✔
3609
                        else {
33,500✔
3610
                            accumulate<T>(op, alloc, list_ref);
33,500✔
3611
                        }
33,500✔
3612
                    }
222,332✔
3613
                    else {
865,304✔
3614
                        accumulate<T>(op, alloc, list_ref);
865,304✔
3615
                    }
865,304✔
3616
                }
1,086,348✔
3617
                if (op.is_null()) {
1,115,388✔
3618
                    destination.set_null(i);
23,036✔
3619
                }
23,036✔
3620
                else {
1,092,352✔
3621
                    destination.set(i, op.result());
1,092,352✔
3622
                }
1,092,352✔
3623
            }
1,115,388✔
3624
        }
1,115,044✔
3625
    }
1,132,552✔
3626

3627
    std::string description(util::serializer::SerialisationState& state) const override
3628
    {
1,584✔
3629
        return m_columns_collection.description(state) + util::serializer::value_separator + Operation::description();
1,584✔
3630
    }
1,584✔
3631

3632
private:
3633
    template <typename StorageType>
3634
    void accumulate(Operation& op, Allocator& alloc, ref_type list_ref)
3635
    {
1,086,348✔
3636
        BPlusTree<StorageType> list(alloc);
1,086,348✔
3637
        list.init_from_ref(list_ref);
1,086,348✔
3638
        size_t s = list.size();
1,086,348✔
3639
        for (unsigned j = 0; j < s; j++) {
6,433,840✔
3640
            auto v = list.get(j);
5,347,492✔
3641
            if (!value_is_null(v)) {
5,347,492✔
3642
                if constexpr (std::is_same_v<StorageType, util::Optional<T>>) {
4,476,020✔
3643
                    op.accumulate(*v);
3,691,444✔
3644
                }
3,691,444✔
3645
                else {
3,691,444✔
3646
                    op.accumulate(v);
3,691,444✔
3647
                }
3,691,444✔
3648
            }
4,476,020✔
3649
        }
5,347,492✔
3650
    }
1,086,348✔
3651

3652
    Mixed do_dictionary_agg(const Dictionary& dict)
3653
    {
17,376✔
3654
        if constexpr (std::is_same_v<Operation, aggregate_operations::Maximum<Mixed>>) {
17,376✔
3655
            return *dict.do_max();
12,060✔
3656
        }
12,060✔
3657
        else if constexpr (std::is_same_v<Operation, aggregate_operations::Minimum<Mixed>>) {
12,060✔
3658
            return *dict.do_min();
8,064✔
3659
        }
8,064✔
3660
        else if constexpr (std::is_same_v<Operation, aggregate_operations::Average<Mixed>>) {
8,064✔
3661
            return *dict.do_avg();
4,068✔
3662
        }
4,068✔
3663
        else if constexpr (std::is_same_v<Operation, aggregate_operations::Sum<Mixed>>) {
4,068✔
3664
            return *dict.do_sum();
4,068✔
3665
        }
4,068✔
3666
        REALM_UNREACHABLE();
×
3667
    }
×
3668

3669
    inline void set_value_for_empty_dictionary(ValueBase& destination, size_t ndx)
3670
    {
1,212✔
3671
        if constexpr (std::is_same_v<Operation, aggregate_operations::Sum<Mixed>>) {
1,212✔
3672
            destination.set(ndx, 0); // the sum of nothing is zero
828✔
3673
        }
828✔
3674
        else {
828✔
3675
            destination.set_null(ndx);
828✔
3676
        }
828✔
3677
    }
1,212✔
3678

3679
    ColumnsCollection<T> m_columns_collection;
3680
    util::Optional<DataType> m_dictionary_key_type;
3681
};
3682

3683
template <class Operator>
3684
Query compare(const Subexpr2<Link>& left, const Obj& obj)
3685
{
280✔
3686
    static_assert(std::is_same_v<Operator, Equal> || std::is_same_v<Operator, NotEqual>,
280✔
3687
                  "Links can only be compared for equality.");
280✔
3688
    const Columns<Link>* column = dynamic_cast<const Columns<Link>*>(&left);
280✔
3689
    if (column) {
280✔
3690
        const LinkMap& link_map = column->link_map();
280✔
3691
        REALM_ASSERT(link_map.get_target_table()->get_key() == obj.get_table()->get_key());
280✔
3692
#ifdef REALM_OLDQUERY_FALLBACK
280✔
3693
        if (link_map.get_nb_hops() == 1) {
280✔
3694
            // We can fall back to Query::links_to for != and == operations on links
137✔
3695
            if (link_map.m_link_types[0] == col_type_Link || (link_map.m_link_types[0] == col_type_LinkList)) {
274✔
3696
                ConstTableRef t = column->get_base_table();
272✔
3697
                Query query(t);
272✔
3698

136✔
3699
                if (std::is_same_v<Operator, Equal>) {
272✔
3700
                    return query.equal(link_map.m_link_column_keys[0], obj.get_link());
166✔
3701
                }
166✔
3702
                else {
106✔
3703
                    return query.not_equal(link_map.m_link_column_keys[0], obj.get_link());
106✔
3704
                }
106✔
3705
            }
8✔
3706
        }
274✔
3707
#endif
280✔
3708
    }
280✔
3709
    return make_expression<Compare<Operator>>(left.clone(), make_subexpr<KeyValue>(obj.get_key()));
8✔
3710
}
8✔
3711
template <class Operator>
3712
Query compare(const Subexpr2<Link>& left, null)
3713
{
42✔
3714
    static_assert(std::is_same_v<Operator, Equal> || std::is_same_v<Operator, NotEqual>,
42✔
3715
                  "Links can only be compared for equality.");
42✔
3716
    return make_expression<Compare<Operator>>(left.clone(), make_subexpr<KeyValue>(ObjKey{}));
42✔
3717
}
42✔
3718

3719

3720
template <class T>
3721
class Columns : public ObjPropertyExpr<T> {
3722
    constexpr static bool requires_null_column = realm::is_any_v<T, int64_t, bool>;
3723

3724
public:
3725
    using LeafType = typename ColumnTypeTraits<T>::cluster_leaf_type;
3726
    using NullableLeafType =
3727
        std::conditional_t<requires_null_column, typename ColumnTypeTraits<util::Optional<T>>::cluster_leaf_type,
3728
                           LeafType>;
3729
    using ObjPropertyExpr<T>::links_exist;
3730
    using ObjPropertyBase::is_nullable;
3731

3732
    Columns(ColKey column, ConstTableRef table, const std::vector<ExtendedColumnKey>& links = {},
3733
            util::Optional<ExpressionComparisonType> type = util::none)
3734
        : ObjPropertyExpr<T>(column, table, links, type)
3735
    {
44,118✔
3736
    }
44,118✔
3737

3738
    Columns(const Columns& other)
3739
        : ObjPropertyExpr<T>(other)
3740
    {
25,428✔
3741
    }
25,428✔
3742

3743
    void set_cluster(const Cluster* cluster) override
3744
    {
22,272✔
3745
        if (links_exist()) {
22,272✔
3746
            m_link_map.set_cluster(cluster);
2,416✔
3747
            m_leaf = mpark::monostate();
2,416✔
3748
        }
2,416✔
3749
        else if (requires_null_column && is_nullable()) {
19,856!
3750
            auto& leaf = m_leaf.template emplace<NullableLeafType>(this->get_base_table()->get_alloc());
3,328✔
3751
            cluster->init_leaf(m_column_key, &leaf);
3,328✔
3752
        }
3,328✔
3753
        else {
16,528✔
3754
            auto& leaf = m_leaf.template emplace<LeafType>(this->get_base_table()->get_alloc());
16,528✔
3755
            cluster->init_leaf(m_column_key, &leaf);
16,528✔
3756
        }
16,528✔
3757
    }
22,272✔
3758

3759
    template <class LeafType2 = LeafType>
3760
    void evaluate_internal(size_t index, ValueBase& destination)
3761
    {
1,074,602✔
3762
        using U = typename LeafType2::value_type;
1,074,602✔
3763

545,748✔
3764
        if (links_exist()) {
1,074,602✔
3765
            REALM_ASSERT(mpark::holds_alternative<mpark::monostate>(m_leaf));
69,628✔
3766
            if (m_link_map.only_unary_links()) {
69,628✔
3767
                destination.init(false, 1);
20,136✔
3768
                destination.set_null(0);
20,136✔
3769
                if (auto link_translation_key = m_link_map.get_unary_link_or_not_found(index)) {
20,136✔
3770
                    const Obj obj = m_link_map.get_target_table()->get_object(link_translation_key);
19,688✔
3771
                    if (!obj.is_null(m_column_key))
19,688!
3772
                        destination.set(0, obj.get<U>(m_column_key));
18,304✔
3773
                }
19,688✔
3774
            }
20,136✔
3775
            else {
49,492✔
3776
                // LinkList with more than 0 values. Create Value with payload for all fields
24,746✔
3777
                std::vector<ObjKey> links = m_link_map.get_links(index);
49,492✔
3778
                destination.init_for_links(m_link_map.only_unary_links(), links.size());
49,492✔
3779

24,746✔
3780
                for (size_t t = 0; t < links.size(); t++) {
148,576✔
3781
                    const Obj obj = m_link_map.get_target_table()->get_object(links[t]);
99,084✔
3782
                    if (obj.is_null(m_column_key))
99,084!
3783
                        destination.set_null(t);
4✔
3784
                    else
99,080✔
3785
                        destination.set(t, obj.get<U>(m_column_key));
99,080✔
3786
                }
99,084✔
3787
            }
49,492✔
3788
        }
69,628✔
3789
        else {
1,004,974✔
3790
            auto leaf = mpark::get_if<LeafType2>(&m_leaf);
1,004,974✔
3791
            REALM_ASSERT(leaf);
1,004,974!
3792
            // Not a Link column
510,934✔
3793
            size_t colsize = leaf->size();
1,004,974✔
3794

510,934✔
3795
            size_t rows = std::min(colsize - index, ValueBase::chunk_size);
1,004,974✔
3796

510,934✔
3797
            // Now load `ValueBase::chunk_size` rows from from the leaf into m_storage.
510,934✔
3798
            if constexpr (std::is_same_v<U, int64_t>) {
1,004,974✔
3799
                // If it's an integer leaf, then it contains the method get_chunk() which copies
395,354✔
3800
                // these values in a super fast way. If you want to modify 'chunk_size' then update Array::get_chunk()
395,354✔
3801
                REALM_ASSERT_3(ValueBase::chunk_size, ==, 8);
773,812✔
3802

395,354✔
3803
                int64_t res[ValueBase::chunk_size];
773,812✔
3804
                static_cast<const Array*>(leaf)->get_chunk(index, res);
773,812✔
3805
                destination.init(false, rows);
773,812✔
3806
                destination.set(res, res + rows);
773,812✔
3807
                return;
773,812✔
3808
            }
773,812✔
3809

UNCOV
3810
            destination.init(false, rows);
×
3811
            for (size_t t = 0; t < rows; t++) {
1,882,604!
3812
                if (leaf->is_null(index + t)) {
1,651,442!
3813
                    destination.set_null(t);
778,178✔
3814
                }
778,178✔
3815
                else {
873,264✔
3816
                    destination.set(t, leaf->get(index + t));
873,264✔
3817
                }
873,264✔
3818
            }
1,651,442✔
UNCOV
3819
        }
×
3820
    }
1,074,602✔
3821

3822
    std::string description(util::serializer::SerialisationState& state) const override
3823
    {
2,084✔
3824
        return state.describe_expression_type(this->m_comparison_type) +
2,084✔
3825
               state.describe_columns(m_link_map, m_column_key);
2,084✔
3826
    }
2,084✔
3827

3828
    // Load values from Column into destination
3829
    void evaluate(size_t index, ValueBase& destination) override
3830
    {
1,074,590✔
3831
        if (is_nullable()) {
1,074,590✔
3832
            evaluate_internal<NullableLeafType>(index, destination);
198,420✔
3833
        }
198,420✔
3834
        else {
876,170✔
3835
            evaluate_internal<LeafType>(index, destination);
876,170✔
3836
        }
876,170✔
3837
    }
1,074,590✔
3838

3839
    void evaluate(ObjKey key, ValueBase& destination) override
3840
    {
38,040✔
3841
        destination.init(false, 1);
38,040✔
3842
        auto table = m_link_map.get_target_table();
38,040✔
3843
        auto obj = table.unchecked_ptr()->get_object(key);
38,040✔
3844
        if (requires_null_column && is_nullable()) {
38,040!
3845
            destination.set(0, obj.template get<util::Optional<T>>(m_column_key));
1,224✔
3846
        }
1,224✔
3847
        else {
36,816✔
3848
            destination.set(0, obj.template get<T>(m_column_key));
36,816✔
3849
        }
36,816✔
3850
    }
38,040✔
3851

3852
private:
3853
    using ObjPropertyExpr<T>::m_link_map;
3854
    using ObjPropertyExpr<T>::m_column_key;
3855
    using LeafStorage =
3856
        std::conditional_t<requires_null_column, mpark::variant<mpark::monostate, LeafType, NullableLeafType>,
3857
                           mpark::variant<mpark::monostate, LeafType>>;
3858
    LeafStorage m_leaf;
3859
};
3860

3861
template <typename T, typename Operation>
3862
class SubColumnAggregate;
3863

3864
// Defines a uniform interface for aggregation methods.
3865
class SubColumnBase {
3866
public:
3867
    virtual std::unique_ptr<Subexpr> max_of() = 0;
3868
    virtual std::unique_ptr<Subexpr> min_of() = 0;
3869
    virtual std::unique_ptr<Subexpr> sum_of() = 0;
3870
    virtual std::unique_ptr<Subexpr> avg_of() = 0;
3871
};
3872

3873
template <typename T>
3874
class SubColumns : public Subexpr, public SubColumnBase {
3875
public:
3876
    SubColumns(Columns<T>&& column, const LinkMap& link_map)
3877
        : m_column(std::move(column))
3878
        , m_link_map(link_map)
3879
    {
2,964✔
3880
    }
2,964✔
3881

3882
    DataType get_type() const final
3883
    {
×
3884
        return ColumnTypeTraits<T>::id;
×
3885
    }
×
3886

3887
    std::unique_ptr<Subexpr> clone() const override
3888
    {
688✔
3889
        return make_subexpr<SubColumns<T>>(*this);
688✔
3890
    }
688✔
3891

3892
    ConstTableRef get_base_table() const override
3893
    {
×
3894
        return m_link_map.get_base_table();
×
3895
    }
×
3896

3897
    void set_base_table(ConstTableRef table) override
3898
    {
×
3899
        m_link_map.set_base_table(table);
×
3900
        m_column.set_base_table(m_link_map.get_target_table());
×
3901
    }
×
3902

3903
    void collect_dependencies(std::vector<TableKey>& tables) const override
3904
    {
×
3905
        m_link_map.collect_dependencies(tables);
×
3906
    }
×
3907

3908
    void evaluate(size_t, ValueBase&) override
3909
    {
×
3910
        // SubColumns can only be used in an expression in conjunction with its aggregate methods.
3911
        REALM_ASSERT(false);
×
3912
    }
×
3913

3914
    std::string description(util::serializer::SerialisationState&) const override
3915
    {
×
3916
        return ""; // by itself there are no conditions, see SubColumnAggregate
×
3917
    }
×
3918

3919
    SubColumnAggregate<T, aggregate_operations::Minimum<T>> min() const
3920
    {
744✔
3921
        return {m_column, m_link_map};
744✔
3922
    }
744✔
3923

3924
    SubColumnAggregate<T, aggregate_operations::Maximum<T>> max() const
3925
    {
768✔
3926
        return {m_column, m_link_map};
768✔
3927
    }
768✔
3928

3929
    SubColumnAggregate<T, aggregate_operations::Sum<T>> sum() const
3930
    {
696✔
3931
        return {m_column, m_link_map};
696✔
3932
    }
696✔
3933

3934
    SubColumnAggregate<T, aggregate_operations::Average<T>> average() const
3935
    {
748✔
3936
        return {m_column, m_link_map};
748✔
3937
    }
748✔
3938

3939
    std::unique_ptr<Subexpr> max_of() override
3940
    {
200✔
3941
        if constexpr (realm::is_any_v<T, Int, Float, Double, Decimal128, Timestamp>) {
200✔
3942
            return max().clone();
200✔
3943
        }
200✔
3944
        else {
200✔
3945
            return {};
200✔
3946
        }
200✔
3947
    }
200✔
3948
    std::unique_ptr<Subexpr> min_of() override
3949
    {
192✔
3950
        if constexpr (realm::is_any_v<T, Int, Float, Double, Decimal128, Timestamp>) {
192✔
3951
            return min().clone();
192✔
3952
        }
192✔
3953
        else {
192✔
3954
            return {};
192✔
3955
        }
192✔
3956
    }
192✔
3957
    std::unique_ptr<Subexpr> sum_of() override
3958
    {
132✔
3959
        if constexpr (realm::is_any_v<T, Int, Float, Double, Decimal128>) {
132✔
3960
            return sum().clone();
4✔
3961
        }
4✔
3962
        else {
4✔
3963
            return {};
4✔
3964
        }
4✔
3965
    }
132✔
3966
    std::unique_ptr<Subexpr> avg_of() override
3967
    {
164✔
3968
        if constexpr (realm::is_any_v<T, Int, Float, Double, Decimal128>) {
164✔
3969
            return average().clone();
4✔
3970
        }
4✔
3971
        else {
4✔
3972
            return {};
4✔
3973
        }
4✔
3974
    }
164✔
3975

3976
private:
3977
    Columns<T> m_column;
3978
    LinkMap m_link_map;
3979
};
3980

3981
template <typename T, typename Operation>
3982
class SubColumnAggregate : public Subexpr2<decltype(Operation().result())> {
3983
public:
3984
    SubColumnAggregate(const Columns<T>& column, const LinkMap& link_map)
3985
        : m_column(column)
3986
        , m_link_map(link_map)
3987
    {
2,956✔
3988
    }
2,956✔
3989
    SubColumnAggregate(SubColumnAggregate const& other)
3990
        : m_column(other.m_column)
3991
        , m_link_map(other.m_link_map)
3992
    {
5,736✔
3993
    }
5,736✔
3994

3995
    std::unique_ptr<Subexpr> clone() const override
3996
    {
5,736✔
3997
        return make_subexpr<SubColumnAggregate>(*this);
5,736✔
3998
    }
5,736✔
3999

4000
    ConstTableRef get_base_table() const override
4001
    {
2,956✔
4002
        return m_link_map.get_base_table();
2,956✔
4003
    }
2,956✔
4004

4005
    void set_base_table(ConstTableRef table) override
4006
    {
3,436✔
4007
        m_link_map.set_base_table(table);
3,436✔
4008
        m_column.set_base_table(m_link_map.get_target_table());
3,436✔
4009
    }
3,436✔
4010

4011
    void set_cluster(const Cluster* cluster) override
4012
    {
4,348✔
4013
        m_link_map.set_cluster(cluster);
4,348✔
4014
    }
4,348✔
4015

4016
    void collect_dependencies(std::vector<TableKey>& tables) const override
4017
    {
436✔
4018
        m_link_map.collect_dependencies(tables);
436✔
4019
    }
436✔
4020

4021
    void evaluate(size_t index, ValueBase& destination) override
4022
    {
59,076✔
4023
        std::vector<ObjKey> keys = m_link_map.get_links(index);
59,076✔
4024
        std::sort(keys.begin(), keys.end());
59,076✔
4025

29,538✔
4026
        Operation op;
59,076✔
4027
        for (auto key : keys) {
49,718✔
4028
            Value<T> value;
40,360✔
4029
            m_column.evaluate(key, value);
40,360✔
4030
            size_t value_index = 0;
40,360✔
4031
            if (!value[value_index].is_null()) {
40,360✔
4032
                op.accumulate(value[value_index].template get<T>());
39,964✔
4033
            }
39,964✔
4034
        }
40,360✔
4035
        if (op.is_null()) {
59,076✔
4036
            destination.set_null(0);
20,972✔
4037
        }
20,972✔
4038
        else {
38,104✔
4039
            destination.set(0, op.result());
38,104✔
4040
        }
38,104✔
4041
    }
59,076✔
4042

4043
    std::string description(util::serializer::SerialisationState& state) const override
4044
    {
340✔
4045
        util::serializer::SerialisationState empty_state(state.group);
340✔
4046
        return state.describe_columns(m_link_map, ColKey()) + util::serializer::value_separator +
340✔
4047
               Operation::description() + util::serializer::value_separator + m_column.description(empty_state);
340✔
4048
    }
340✔
4049

4050
private:
4051
    Columns<T> m_column;
4052
    LinkMap m_link_map;
4053
};
4054

4055
class SubQueryCount : public Subexpr2<Int> {
4056
public:
4057
    SubQueryCount(const Query& q, const LinkMap& link_map)
4058
        : m_query(q)
4059
        , m_link_map(link_map)
4060
    {
412✔
4061
        REALM_ASSERT(q.produces_results_in_table_order());
412✔
4062
        REALM_ASSERT(m_query.get_table() == m_link_map.get_target_table());
412✔
4063
    }
412✔
4064

4065
    ConstTableRef get_base_table() const override
4066
    {
412✔
4067
        return m_link_map.get_base_table();
412✔
4068
    }
412✔
4069

4070
    void set_base_table(ConstTableRef table) override
4071
    {
440✔
4072
        m_link_map.set_base_table(table);
440✔
4073
        m_query.set_table(m_link_map.get_target_table().cast_away_const());
440✔
4074
    }
440✔
4075

4076
    void set_cluster(const Cluster* cluster) override
4077
    {
588✔
4078
        m_link_map.set_cluster(cluster);
588✔
4079
    }
588✔
4080

4081
    void collect_dependencies(std::vector<TableKey>& tables) const override
4082
    {
32✔
4083
        m_link_map.collect_dependencies(tables);
32✔
4084
    }
32✔
4085

4086
    void evaluate(size_t index, ValueBase& destination) override
4087
    {
3,928✔
4088
        std::vector<ObjKey> links = m_link_map.get_links(index);
3,928✔
4089
        // std::sort(links.begin(), links.end());
1,964✔
4090
        if (!m_initialized) {
3,928✔
4091
            m_query.init();
452✔
4092
            m_initialized = true;
452✔
4093
        }
452✔
4094

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

1,964✔
4100
        destination = Value<int64_t>(count);
3,928✔
4101
    }
3,928✔
4102

4103
    std::string description(util::serializer::SerialisationState& state) const override
4104
    {
144✔
4105
        REALM_ASSERT(m_link_map.get_base_table() != nullptr);
144✔
4106
        std::string target = state.describe_columns(m_link_map, ColKey());
144✔
4107
        std::string var_name = state.get_variable_name(m_link_map.get_base_table());
144✔
4108
        state.subquery_prefix_list.push_back(var_name);
144✔
4109
        std::string desc = "SUBQUERY(" + target + ", " + var_name + ", " + m_query.get_description(state) + ")" +
144✔
4110
                           util::serializer::value_separator + "@count";
144✔
4111
        state.subquery_prefix_list.pop_back();
144✔
4112
        return desc;
144✔
4113
    }
144✔
4114

4115
    std::unique_ptr<Subexpr> clone() const override
4116
    {
560✔
4117
        return make_subexpr<SubQueryCount>(*this);
560✔
4118
    }
560✔
4119

4120
    SubQueryCount(const SubQueryCount& other)
4121
        : m_query(other.m_query)
4122
        , m_link_map(other.m_link_map)
4123
        , m_initialized(false)
4124
    {
560✔
4125
    }
560✔
4126

4127
private:
4128
    Query m_query;
4129
    LinkMap m_link_map;
4130
    bool m_initialized = false;
4131
};
4132

4133
// The unused template parameter is a hack to avoid a circular dependency between table.hpp and query_expression.hpp.
4134
template <class>
4135
class SubQuery {
4136
public:
4137
    SubQuery(Columns<Link> link_column, Query query)
4138
        : m_query(std::move(query))
4139
        , m_link_map(link_column.link_map())
4140
    {
66✔
4141
        REALM_ASSERT(m_link_map.get_target_table() == m_query.get_table());
66✔
4142
    }
66✔
4143

4144
    SubQueryCount count() const
4145
    {
66✔
4146
        return SubQueryCount(m_query, m_link_map);
66✔
4147
    }
66✔
4148

4149
private:
4150
    Query m_query;
4151
    LinkMap m_link_map;
4152
};
4153

4154
template <class oper>
4155
class Operator : public Subexpr2<Mixed> {
4156
public:
4157
    Operator(std::unique_ptr<Subexpr> left, std::unique_ptr<Subexpr> right)
4158
        : m_left(std::move(left))
4159
        , m_right(std::move(right))
4160
    {
992✔
4161
        m_left_is_const = m_left->has_single_value();
992✔
4162
        m_right_is_const = m_right->has_single_value();
992✔
4163
        if (m_left_is_const) {
992✔
4164
            m_const_value = m_left->get_mixed();
152✔
4165
        }
152✔
4166
        else if (m_right_is_const) {
840✔
4167
            m_const_value = m_right->get_mixed();
412✔
4168
        }
412✔
4169
    }
992✔
4170

4171
    Operator(const Operator& other)
4172
        : m_left(other.m_left->clone())
4173
        , m_right(other.m_right->clone())
4174
        , m_left_is_const(other.m_left_is_const)
4175
        , m_right_is_const(other.m_right_is_const)
4176
        , m_const_value(other.m_const_value)
4177
    {
1,388✔
4178
    }
1,388✔
4179

4180
    Operator& operator=(const Operator& other)
4181
    {
4182
        if (this != &other) {
4183
            m_left = other.m_left->clone();
4184
            m_right = other.m_right->clone();
4185
        }
4186
        return *this;
4187
    }
4188

4189
    Operator(Operator&&) noexcept = delete;
4190
    Operator& operator=(Operator&&) noexcept = delete;
4191

4192
    DataType get_type() const override
4193
    {
2,920✔
4194
        return m_left->get_type();
2,920✔
4195
    }
2,920✔
4196

4197
    // See comment in base class
4198
    void set_base_table(ConstTableRef table) override
4199
    {
992✔
4200
        m_left->set_base_table(table);
992✔
4201
        m_right->set_base_table(table);
992✔
4202
    }
992✔
4203

4204
    void set_cluster(const Cluster* cluster) override
4205
    {
11,092✔
4206
        m_left->set_cluster(cluster);
11,092✔
4207
        m_right->set_cluster(cluster);
11,092✔
4208
    }
11,092✔
4209

4210
    // Recursively fetch tables of columns in expression tree. Used when user first builds a stand-alone expression
4211
    // and
4212
    // binds it to a Query at a later time
4213
    ConstTableRef get_base_table() const override
4214
    {
992✔
4215
        ConstTableRef l = m_left->get_base_table();
992✔
4216
        ConstTableRef r = m_right->get_base_table();
992✔
4217

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

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

4225
    // destination = operator(left, right)
4226
    void evaluate(size_t index, ValueBase& destination) override
4227
    {
807,016✔
4228
        Value<T> result;
807,016✔
4229
        Value<T> left;
807,016✔
4230
        Value<T> right;
807,016✔
4231
        if (m_left_is_const) {
807,016✔
4232
            m_right->evaluate(index, right);
488✔
4233
            result.template fun_const<oper>(m_const_value, right);
488✔
4234
        }
488✔
4235
        else if (m_right_is_const) {
806,528✔
4236
            m_left->evaluate(index, left);
255,540✔
4237
            result.template fun_const<oper>(left, m_const_value);
255,540✔
4238
        }
255,540✔
4239
        else {
550,988✔
4240
            m_left->evaluate(index, left);
550,988✔
4241
            m_right->evaluate(index, right);
550,988✔
4242
            result.template fun<oper>(left, right);
550,988✔
4243
        }
550,988✔
4244
        destination = result;
807,016✔
4245
    }
807,016✔
4246

4247
    std::string description(util::serializer::SerialisationState& state) const override
4248
    {
204✔
4249
        std::string s = "(";
204✔
4250
        if (m_left) {
204✔
4251
            s += m_left->description(state);
204✔
4252
        }
204✔
4253
        s += (" " + oper::description() + " ");
204✔
4254
        if (m_right) {
204✔
4255
            s += m_right->description(state);
204✔
4256
        }
204✔
4257
        s += ")";
204✔
4258
        return s;
204✔
4259
    }
204✔
4260

4261
    util::Optional<ExpressionComparisonType> get_comparison_type() const override
4262
    {
684,920✔
4263
        if (!m_left_is_const) {
684,920✔
4264
            return m_left->get_comparison_type();
684,616✔
4265
        }
684,616✔
4266
        if (!m_right_is_const) {
304✔
4267
            return m_right->get_comparison_type();
304✔
4268
        }
304✔
4269
        return util::none;
×
4270
    }
×
4271

4272
    std::unique_ptr<Subexpr> clone() const override
4273
    {
1,388✔
4274
        return make_subexpr<Operator>(*this);
1,388✔
4275
    }
1,388✔
4276

4277
private:
4278
    std::unique_ptr<Subexpr> m_left;
4279
    std::unique_ptr<Subexpr> m_right;
4280
    bool m_left_is_const;
4281
    bool m_right_is_const;
4282
    Mixed m_const_value;
4283
};
4284

4285
class CompareBase : public Expression {
4286
public:
4287
    CompareBase(std::unique_ptr<Subexpr> left, std::unique_ptr<Subexpr> right)
4288
        : m_left(std::move(left))
4289
        , m_right(std::move(right))
4290
    {
63,855✔
4291
        if (m_left->has_constant_evaluation()) {
63,855✔
4292
            m_left_const_values = dynamic_cast<ValueBase*>(m_left.get());
26,928✔
4293
        }
26,928✔
4294
        if (m_right->has_constant_evaluation()) {
63,855✔
4295
            m_right_const_values = dynamic_cast<ValueBase*>(m_right.get());
29,028✔
4296
        }
29,028✔
4297
        REALM_ASSERT(!(m_left_const_values && m_right_const_values));
63,855✔
4298
    }
63,855✔
4299

4300
    // See comment in base class
4301
    void set_base_table(ConstTableRef table) override
4302
    {
69,105✔
4303
        m_left->set_base_table(table);
69,105✔
4304
        m_right->set_base_table(table);
69,105✔
4305
    }
69,105✔
4306

4307
    void set_cluster(const Cluster* cluster) override
4308
    {
89,997✔
4309
        if (m_has_matches) {
89,997✔
4310
            m_cluster = cluster;
1,170✔
4311
        }
1,170✔
4312
        else {
88,827✔
4313
            m_left->set_cluster(cluster);
88,827✔
4314
            m_right->set_cluster(cluster);
88,827✔
4315
        }
88,827✔
4316
    }
89,997✔
4317

4318
    // Recursively fetch tables of columns in expression tree. Used when user first builds a stand-alone expression
4319
    // and binds it to a Query at a later time
4320
    ConstTableRef get_base_table() const override
4321
    {
63,858✔
4322
        ConstTableRef l = m_left->get_base_table();
63,858✔
4323
        ConstTableRef r = m_right->get_base_table();
63,858✔
4324

31,929✔
4325
        // All main tables in each subexpression of a query (table.columns() or table.link()) must be the same.
31,929✔
4326
        REALM_ASSERT(l == nullptr || r == nullptr || l == r);
63,858✔
4327

31,929✔
4328
        // nullptr pointer means expression which isn't yet associated with any table, or is a Value<T>
31,929✔
4329
        return (l) ? l : r;
50,373✔
4330
    }
63,858✔
4331

4332
    void collect_dependencies(std::vector<TableKey>& tables) const override
4333
    {
3,162✔
4334
        m_left->collect_dependencies(tables);
3,162✔
4335
        m_right->collect_dependencies(tables);
3,162✔
4336
    }
3,162✔
4337

4338
    size_t find_first_with_matches(size_t start, size_t end) const
4339
    {
15,687✔
4340
        if (m_index_end == 0 || start >= end)
15,687✔
4341
            return not_found;
30✔
4342

7,884✔
4343
        ObjKey first_key = m_cluster->get_real_key(start);
15,657✔
4344
        ObjKey actual_key;
15,657✔
4345

7,884✔
4346
        // Sequential lookup optimization: when the query isn't constrained
7,884✔
4347
        // to a LnkLst we'll get find_first() requests in ascending order,
7,884✔
4348
        // so we can do a simple linear scan.
7,884✔
4349
        if (m_index_get < m_index_end && m_matches[m_index_get] <= first_key) {
15,657✔
4350
            actual_key = m_matches[m_index_get];
14,850✔
4351
            // skip through keys which are in "earlier" leafs than the one selected by start..end:
7,482✔
4352
            while (first_key > actual_key) {
28,668✔
4353
                m_index_get++;
14,577✔
4354
                if (m_index_get == m_index_end)
14,577✔
4355
                    return not_found;
759✔
4356
                actual_key = m_matches[m_index_get];
13,818✔
4357
            }
13,818✔
4358
        }
14,850✔
4359
        // Otherwise if we get requests out of order we have to do a more
402✔
4360
        // expensive binary search
402✔
4361
        else {
807✔
4362
            auto it = std::lower_bound(m_matches.begin(), m_matches.end(), first_key);
807✔
4363
            if (it == m_matches.end())
807✔
4364
                return not_found;
×
4365
            actual_key = *it;
807✔
4366
        }
807✔
4367

7,884✔
4368
        // if actual key is bigger than last key, it is not in this leaf
7,884✔
4369
        ObjKey last_key = start + 1 == end ? first_key : m_cluster->get_real_key(end - 1);
15,276✔
4370
        if (actual_key > last_key)
14,898✔
4371
            return not_found;
78✔
4372

7,467✔
4373
        // key is known to be in this leaf, so find key whithin leaf keys
7,467✔
4374
        return m_cluster->lower_bound_key(ObjKey(actual_key.value - m_cluster->get_offset()));
14,820✔
4375
    }
14,820✔
4376

4377
protected:
4378
    CompareBase(const CompareBase& other)
4379
        : m_left(other.m_left->clone())
4380
        , m_right(other.m_right->clone())
4381
    {
58,680✔
4382
        if (m_left->has_constant_evaluation()) {
58,680✔
4383
            m_left_const_values = dynamic_cast<ValueBase*>(m_left.get());
20,256✔
4384
        }
20,256✔
4385
        if (m_right->has_constant_evaluation()) {
58,680✔
4386
            m_right_const_values = dynamic_cast<ValueBase*>(m_right.get());
30,609✔
4387
        }
30,609✔
4388
    }
58,680✔
4389

4390
    std::unique_ptr<Subexpr> m_left;
4391
    std::unique_ptr<Subexpr> m_right;
4392
    const Cluster* m_cluster;
4393
    ValueBase* m_left_const_values = nullptr;
4394
    ValueBase* m_right_const_values = nullptr;
4395
    bool m_has_matches = false;
4396
    std::vector<ObjKey> m_matches;
4397
    mutable size_t m_index_get = 0;
4398
    size_t m_index_end = 0;
4399
};
4400

4401
template <class TCond>
4402
class Compare : public CompareBase {
4403
public:
4404
    using CompareBase::CompareBase;
4405

4406
    double init() override
4407
    {
68,446✔
4408
        double dT = 50.0;
68,446✔
4409
        if ((m_left->has_single_value()) || (m_right->has_single_value())) {
68,446✔
4410
            dT = 10.0;
57,340✔
4411
            if constexpr (std::is_same_v<TCond, Equal>) {
57,340✔
4412
                // If the property not being constant has a search index we can speed things up by
17,526✔
4413
                // finding all matches up front.
17,526✔
4414
                Mixed const_value;
35,052✔
4415
                Subexpr* column;
35,052✔
4416
                if (m_left->has_single_value()) {
35,052✔
4417
                    const_value = m_left->get_mixed();
14,610✔
4418
                    column = m_right.get();
14,610✔
4419
                }
14,610✔
4420
                else {
20,442✔
4421
                    const_value = m_right->get_mixed();
20,442✔
4422
                    column = m_left.get();
20,442✔
4423
                }
20,442✔
4424

17,526✔
4425
                if (column->has_search_index() && !column->has_indexes_in_link_map() &&
35,052✔
4426
                    column->get_comparison_type().value_or(ExpressionComparisonType::Any) ==
18,210✔
4427
                        ExpressionComparisonType::Any) {
1,344✔
4428
                    if (const_value.is_null()) {
1,320✔
4429
                        const ObjPropertyBase* prop = dynamic_cast<const ObjPropertyBase*>(m_right.get());
348✔
4430
                        // when checking for null across links, null links are considered matches,
174✔
4431
                        // so we must compute the slow matching even if there is an index.
174✔
4432
                        if (!prop || prop->links_exist()) {
348✔
4433
                            return dT;
282✔
4434
                        }
282✔
4435
                        else {
66✔
4436
                            m_matches = column->find_all(Mixed());
66✔
4437
                        }
66✔
4438
                    }
348✔
4439
                    else {
972✔
4440
                        if (column->get_type() != const_value.get_type()) {
972✔
4441
                            // If the type we are looking for is not the same type as the target
3✔
4442
                            // column, we cannot use the index
3✔
4443
                            return dT;
6✔
4444
                        }
6✔
4445
                        m_matches = column->find_all(const_value);
966✔
4446
                    }
966✔
4447
                    // Sort
660✔
4448
                    std::sort(m_matches.begin(), m_matches.end());
1,176✔
4449
                    // Remove all duplicates
516✔
4450
                    m_matches.erase(std::unique(m_matches.begin(), m_matches.end()), m_matches.end());
1,032✔
4451

516✔
4452
                    m_has_matches = true;
1,032✔
4453
                    m_index_get = 0;
1,032✔
4454
                    m_index_end = m_matches.size();
1,032✔
4455
                    dT = 0;
1,032✔
4456
                }
1,032✔
4457
            }
35,052✔
4458
        }
57,340✔
4459

34,222✔
4460
        return dT;
54,016✔
4461
    }
68,446✔
4462

4463
    size_t find_first(size_t start, size_t end) const override
4464
    {
1,604,334✔
4465
        if (m_has_matches) {
1,604,334✔
4466
            return find_first_with_matches(start, end);
15,687✔
4467
        }
15,687✔
4468

795,314✔
4469
        size_t match;
1,588,647✔
4470
        ValueBase left_buf;
1,588,647✔
4471
        ValueBase right_buf;
1,588,647✔
4472
        const util::Optional<ExpressionComparisonType> left_cmp_type = m_left->get_comparison_type();
1,588,647✔
4473
        const util::Optional<ExpressionComparisonType> right_cmp_type = m_right->get_comparison_type();
1,588,647✔
4474

795,314✔
4475
        ValueBase* left = m_left_const_values ? m_left_const_values : &left_buf;
1,415,517✔
4476
        ValueBase* right = m_right_const_values ? m_right_const_values : &right_buf;
1,503,336✔
4477

795,314✔
4478
        for (; start < end;) {
3,086,748✔
4479
            if (!m_left_const_values)
3,027,262✔
4480
                m_left->evaluate(start, left_buf);
542,419✔
4481
            if (!m_right_const_values)
3,027,262✔
4482
                m_right->evaluate(start, right_buf);
2,708,383✔
4483
            match = ValueBase::template compare<TCond>(*left, *right, left_cmp_type, right_cmp_type);
3,027,262✔
4484
            if (match != not_found && match + start < end)
3,027,262✔
4485
                return start + match;
1,529,161✔
4486

748,926✔
4487
            size_t rows = (left->m_from_list || right->m_from_list) ? 1 : std::min(right->size(), left->size());
1,498,101✔
4488
            start += rows;
1,498,101✔
4489
        }
1,498,101✔
4490

795,314✔
4491
        return not_found; // no match
825,073✔
4492
    }
1,588,647✔
4493

4494
    std::string description(util::serializer::SerialisationState& state) const override
4495
    {
17,178✔
4496
        if constexpr (realm::is_any_v<TCond, BeginsWith, BeginsWithIns, EndsWith, EndsWithIns, Contains, ContainsIns,
17,178✔
4497
                                      Like, LikeIns>) {
8,589✔
4498
            // these string conditions have the arguments reversed but the order is important
7,998✔
4499
            // operations ==, and != can be reversed because the produce the same results both ways
7,998✔
4500
            return util::serializer::print_value(util::format("%1 %2 %3", m_right->description(state),
8,589✔
4501
                                                              TCond::description(), m_left->description(state)));
8,589✔
4502
        }
8,589✔
4503
        else {
15,996✔
4504
            state.target_table = m_right->get_target_table();
15,996✔
4505
            std::string ret = m_left->description(state) + " " + TCond::description() + " ";
15,996✔
4506
            state.target_table = m_left->get_target_table();
15,996✔
4507
            ret += m_right->description(state);
15,996✔
4508
            return ret;
15,996✔
4509
        }
15,996✔
4510
    }
17,178✔
4511

4512
    std::unique_ptr<Expression> clone() const override
4513
    {
54,914✔
4514
        return std::unique_ptr<Expression>(new Compare(*this));
54,914✔
4515
    }
54,914✔
4516
};
4517
} // namespace realm
4518
#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