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

realm / realm-core / jorgen.edelbo_334

01 Jul 2024 07:22AM UTC coverage: 90.829% (-0.04%) from 90.865%
jorgen.edelbo_334

Pull #7803

Evergreen

jedelbo
Merge branch 'next-major' into feature/string-compression
Pull Request #7803: Feature/string compression

102912 of 180568 branches covered (56.99%)

1141 of 1267 new or added lines in 33 files covered. (90.06%)

172 existing lines in 24 files now uncovered.

218291 of 240332 relevant lines covered (90.83%)

7818396.4 hits per line

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

93.82
/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(Subexpr::Index& 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(Subexpr::Index& 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(Subexpr::Index& i, ValueBase* destination)
63
    T m_v[8];
64

65
Columns<T>: public Subexpr2
66
    void evaluate(Subexpr::Index& 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(Subexpr::Index& i, ValueBase* destination) method which returns a
86
Value<T> 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(Subexpr::Index& 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
32,230✔
166

167
namespace realm {
168

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

175
struct Plus {
176
    Mixed operator()(Mixed v1, Mixed v2) const
177
    {
6,978,052✔
178
        return v1 + v2;
6,978,052✔
179
    }
6,978,052✔
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
    {
3,696,784✔
189
        return v1 - v2;
3,696,784✔
190
    }
3,696,784✔
191
    static std::string description()
192
    {
16✔
193
        return "-";
16✔
194
    }
16✔
195
};
196

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

208
struct Mul {
209
    Mixed operator()(Mixed v1, Mixed v2) const
210
    {
1,480,624✔
211
        return v1 * v2;
1,480,624✔
212
    }
1,480,624✔
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;
13,717,050✔
249
    ValueBase(const ValueType& init_val)
250
    {
751,746✔
251
        m_first[0] = init_val;
751,746✔
252
    }
751,746✔
253
    ~ValueBase()
254
    {
14,521,617✔
255
        dealloc();
14,521,617✔
256
    }
14,521,617✔
257
    ValueBase(const ValueBase& other)
258
    {
49,458✔
259
        *this = other;
49,458✔
260
    }
49,458✔
261

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

269
    size_t size() const
270
    {
26,794,848✔
271
        return m_size;
26,794,848✔
272
    }
26,794,848✔
273

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

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

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

298
    template <class T>
299
    void set(size_t ndx, const T& val)
300
    {
75,290,941✔
301
        if constexpr (std::is_same<T, float>::value || std::is_same<T, double>::value) {
64,963,078✔
302
            m_first[ndx] = null::is_null_float(val) ? ValueType() : ValueType(val);
41,644,608✔
303
        }
13,236,666✔
304
        else {
74,039,381✔
305
            m_first[ndx] = ValueType(val);
74,039,381✔
306
        }
74,039,381✔
307
    }
75,290,941✔
308

309
    template <class T>
310
    void set(T b, T e)
311
    {
4,746,601✔
312
        size_t i = 0;
4,746,601✔
313
        for (auto from = b; from != e; ++from) {
41,399,662✔
314
            set(i, *from);
36,653,061✔
315
            i++;
36,653,061✔
316
        }
36,653,061✔
317
    }
4,746,601✔
318

319
    ValueType& operator[](size_t n)
320
    {
22,648,779✔
321
        return m_first[n];
22,648,779✔
322
    }
22,648,779✔
323

324
    const ValueType& operator[](size_t n) const
325
    {
22,831,144✔
326
        return m_first[n];
22,831,144✔
327
    }
22,831,144✔
328

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

334
    ValueType* begin()
335
    {
40,308✔
336
        return m_first;
40,308✔
337
    }
40,308✔
338
    const ValueType* begin() const
339
    {
2,671,275✔
340
        return m_first;
2,671,275✔
341
    }
2,671,275✔
342

343
    ValueType* end()
344
    {
1,596✔
345
        return m_first + m_size;
1,596✔
346
    }
1,596✔
347
    const ValueType* end() const
348
    {
2,671,275✔
349
        return m_first + m_size;
2,671,275✔
350
    }
2,671,275✔
351
    void sort()
352
    {
1,236✔
353
        if (!m_sorted) {
1,236✔
354
            std::sort(begin(), end());
1,080✔
355
            m_sorted = true;
1,080✔
356
        }
1,080✔
357
    }
1,236✔
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
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
    {
562,152✔
372
        TOperator o;
562,152✔
373
        // Operate on values one-by-one
374
        size_t sz = left.size();
562,152✔
375
        init(left.m_from_list, sz);
562,152✔
376
        for (size_t i = 0; i < sz; i++) {
5,000,496✔
377
            set(i, o(left[i], const_value));
4,438,344✔
378
        }
4,438,344✔
379
    }
562,152✔
380
    template <class TOperator>
381
    REALM_FORCEINLINE void fun(const ValueBase& left, const ValueBase& right)
382
    {
1,164,212✔
383
        TOperator o;
1,164,212✔
384

385
        if (!left.m_from_list && !right.m_from_list) {
1,164,212✔
386
            // Operate on values one-by-one (one value is one row; no links)
387
            size_t min = std::min(left.size(), right.size());
1,164,208✔
388
            init(false, min);
1,164,208✔
389

390
            for (size_t i = 0; i < min; i++) {
10,360,072✔
391
                set(i, o(left[i], right[i]));
9,195,864✔
392
            }
9,195,864✔
393
        }
1,164,208✔
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
396
            // Eg: `{1, 2, 3} * {4, 5} > age`
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
    }
1,164,212✔
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,989,030✔
427
        TCond c;
3,989,030✔
428
        using Compare = ExpressionComparisonType;
3,989,030✔
429

430
        if (!left.m_from_list && !right.m_from_list) {
3,989,030✔
431
            // ALL/NONE not supported for non list types
432
            REALM_ASSERT_DEBUG(!left_cmp_type || *left_cmp_type == Compare::Any);
3,640,039✔
433
            REALM_ASSERT_DEBUG(!right_cmp_type || *right_cmp_type == Compare::Any);
3,640,039!
434
            // Compare values one-by-one (one value is one row; no link lists)
435
            size_t min = minimum(left.size(), right.size());
3,640,039✔
436
            for (size_t m = 0; m < min; m++) {
11,438,233✔
437
                if (c(left[m], right[m]))
9,294,132✔
438
                    return m;
1,495,938✔
439
            }
9,294,132✔
440
            return not_found;
2,144,101✔
441
        }
3,640,039✔
442

443
        if (left.m_from_list && right.m_from_list && !left_cmp_type && !right_cmp_type) {
348,991!
444
            // Both lists and no ANY, NONE, ALL specified - simple element by element comparison
445
            if (left.size() != right.size()) {
4,132!
446
                if constexpr (std::is_same_v<TCond, NotEqual>) {
2,568✔
447
                    return 0; // mismatch size
1,284✔
448
                }
1,158✔
449
                else {
2,316✔
450
                    return not_found;
2,316✔
451
                }
2,316✔
452
            }
2,568✔
453
            for (size_t i = 0; i < left.size(); ++i) {
5,052!
454
                if (!c(left[i], right[i])) {
3,200✔
455
                    return not_found;
996✔
456
                }
996✔
457
            }
3,200✔
458
            return 0; // all elements matched in the right order
1,852✔
459
        }
2,848✔
460

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

465
        size_t left_size = left.m_from_list ? left.size() : 1;
344,859✔
466
        size_t right_size = right.m_from_list ? right.size() : 1;
344,859✔
467

468
        if (left_size > 2 && right_size > 2) {
344,859!
469
            left.sort();
548✔
470
            right.sort();
548✔
471

472
            if constexpr (std::is_same_v<TCond, Equal>) {
548✔
473
                if (compare_left != ExpressionComparisonType::None && compare_right == Compare::Any) {
408✔
474
                    // Optimization with O(n) complexity
475
                    const bool any = compare_left == ExpressionComparisonType::Any;
198✔
476
                    size_t left_idx = 0;
198✔
477
                    size_t right_idx = 0;
198✔
478
                    while (right_idx < right_size) {
1,200✔
479
                        if (c(left[left_idx], right[right_idx])) {
1,140✔
480
                            left_idx++;
300✔
481
                            right_idx++;
300✔
482
                            if (any || left_idx == left_size) {
300✔
483
                                return 0;
78✔
484
                            }
78✔
485
                        }
300✔
486
                        else {
840✔
487
                            if (left[left_idx] < right[right_idx]) {
840✔
488
                                if (any && left_idx < left_size - 1) {
444✔
489
                                    left_idx++;
384✔
490
                                }
384✔
491
                                else {
60✔
492
                                    return not_found;
60✔
493
                                }
60✔
494
                            }
444✔
495
                            else {
396✔
496
                                right_idx++;
396✔
497
                            }
396✔
498
                        }
840✔
499
                    }
1,140✔
500
                    return not_found;
60✔
501
                }
198✔
502
            }
204✔
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];
140✔
506
                left_size = 2;
140✔
507
                right[1] = right[right_size - 1];
140✔
508
                right_size = 2;
140✔
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
        }
548✔
516

517
        if constexpr (realm::is_any_v<TCond, BeginsWith, BeginsWithIns, EndsWith, EndsWithIns, Contains, ContainsIns,
172,346✔
518
                                      Like, LikeIns>) {
184,685✔
519
            // The string operators have the arguments reversed so we have to iterate right in the
520
            // outer loop as this is actually the left argument
521
            auto left_matches = [&](const QueryValue& right_val) {
176,060✔
522
                for (size_t i = 0; i < left_size; i++) {
58,554✔
523
                    if (c(left[i], right_val)) {
33,066✔
524
                        // match
525
                        if (compare_left == Compare::Any) {
6,402✔
526
                            return true;
6,174✔
527
                        }
6,174✔
528
                        if (compare_left == Compare::None) {
228!
529
                            return false; // one matched
60✔
530
                        }
60✔
531
                    }
228✔
532
                    else {
26,664✔
533
                        // no match
534
                        if (compare_left == Compare::All) {
26,664✔
535
                            return false;
384✔
536
                        }
384✔
537
                    }
26,664✔
538
                }
33,066✔
539
                return compare_left == Compare::None || compare_left == Compare::All;
25,488✔
540
            };
32,106✔
541

542
            for (size_t i = 0; i < right_size; i++) {
185,090✔
543
                if (left_matches(right[i])) {
32,106✔
544
                    if (compare_right == Compare::Any) {
6,354✔
545
                        return 0;
3,666✔
546
                    }
3,666✔
547
                    if (compare_right == Compare::None) {
2,688✔
548
                        return not_found; // one matched
1,440✔
549
                    }
1,440✔
550
                }
2,688✔
551
                else {
25,752✔
552
                    if (compare_right == Compare::All) {
25,752✔
553
                        return not_found;
1,512✔
554
                    }
1,512✔
555
                }
25,752✔
556
            }
32,106✔
557
            if (compare_right == Compare::None || compare_right == Compare::All) {
18,060✔
558
                return 0; // either none or all
2,760✔
559
            }
2,760✔
560
        }
9,030✔
561
        else {
320,082✔
562
            auto right_matches = [&](const QueryValue& left_val) {
472,185✔
563
                for (size_t i = 0; i < right_size; i++) {
830,454✔
564
                    if (c(left_val, right[i])) {
500,005✔
565
                        // match
566
                        if (compare_right == Compare::Any) {
144,598✔
567
                            return true;
136,184✔
568
                        }
136,184✔
569
                        if (compare_right == Compare::None) {
8,414✔
570
                            return false; // one matched
2,690✔
571
                        }
2,690✔
572
                    }
8,414✔
573
                    else {
355,407✔
574
                        // no match
575
                        if (compare_right == Compare::All) {
355,407✔
576
                            return false;
2,862✔
577
                        }
2,862✔
578
                    }
355,407✔
579
                }
500,005✔
580
                return compare_right == Compare::None || compare_right == Compare::All;
330,449✔
581
            };
472,185✔
582

583
            for (size_t i = 0; i < left_size; i++) {
650,951✔
584
                if (right_matches(left[i])) {
472,188✔
585
                    if (compare_left == Compare::Any) {
143,101✔
586
                        return 0;
115,653✔
587
                    }
115,653✔
588
                    if (compare_left == ExpressionComparisonType::None) {
27,448✔
589
                        return not_found; // one matched
11,652✔
590
                    }
11,652✔
591
                }
27,448✔
592
                else {
329,087✔
593
                    if (compare_left == ExpressionComparisonType::All) {
329,087✔
594
                        return not_found;
14,014✔
595
                    }
14,014✔
596
                }
329,087✔
597
            }
472,188✔
598
            if (compare_left == ExpressionComparisonType::None || compare_left == ExpressionComparisonType::All) {
178,763✔
599
                return 0; // either none or all
28,584✔
600
            }
28,584✔
601
        }
178,763✔
602

603
        return not_found; // no match
165,479✔
604
    }
300,945✔
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
    {
14,346,084✔
618
        if (size == m_size)
14,346,084✔
619
            return;
6,168,330✔
620

621
        dealloc();
8,177,754✔
622
        m_size = size;
8,177,754✔
623
        if (m_size > 0) {
8,177,754✔
624
            if (m_size > prealloc)
7,976,700✔
625
                m_first = new QueryValue[m_size];
15,534✔
626
            else
7,961,166✔
627
                m_first = &m_cache[0];
7,961,166✔
628
        }
7,976,700✔
629
    }
8,177,754✔
630
    void dealloc()
631
    {
22,696,461✔
632
        if (m_first) {
22,696,461✔
633
            if (m_size > prealloc)
22,484,358✔
634
                delete[] m_first;
15,534✔
635
            m_first = nullptr;
22,484,358✔
636
        }
22,484,358✔
637
    }
22,696,461✔
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;
143,838✔
649

650
    virtual double init()
651
    {
1,998✔
652
        return 50.0; // Default dT
1,998✔
653
    }
1,998✔
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,422✔
668
    return std::unique_ptr<Expression>(new T(std::forward<Args>(args)...));
25,422✔
669
}
25,422✔
670

671
class Subexpr {
672
public:
673
    class Index {
674
    public:
675
        Index()
676
            : Index(0)
677
        {
×
678
        }
×
679
        Index(size_t start)
680
            : row_index(start)
4,147,386✔
681
        {
8,378,673✔
682
        }
8,378,673✔
683
        bool initialize() const
684
        {
252,990✔
685
            return sub_index == 0;
252,990✔
686
        }
252,990✔
687
        operator size_t() const
688
        {
7,440,306✔
689
            return row_index;
7,440,306✔
690
        }
7,440,306✔
691
        explicit operator bool() = delete;
692
        bool more() const
693
        {
4,859,748✔
694
            return sub_index < sub_size;
4,859,748✔
695
        }
4,859,748✔
696
        bool set_size(size_t s)
697
        {
249,636✔
698
            sub_size = s;
249,636✔
699
            return sub_size != 0;
249,636✔
700
        }
249,636✔
701
        size_t get_and_incr_sub_index()
702
        {
252,378✔
703
            return sub_index++;
252,378✔
704
        }
252,378✔
705

706
    private:
707
        size_t row_index;
708
        size_t sub_index = 0;
709
        size_t sub_size = 0;
710
    };
711
    virtual ~Subexpr() = default;
11,875,059✔
712

713
    virtual std::unique_ptr<Subexpr> clone() const = 0;
714

715
    // When the user constructs a query, it always "belongs" to one single base/parent table (regardless of
716
    // any links or not and regardless of any queries assembled with || or &&). When you do a Query::find(),
717
    // then Query::m_table is set to this table, and set_base_table() is called on all Columns and LinkMaps in
718
    // the query expression tree so that they can set/update their internals as required.
719
    //
720
    // During thread-handover of a Query, set_base_table() is also called to make objects point at the new table
721
    // instead of the old one from the old thread.
722
    virtual void set_base_table(ConstTableRef) {}
66,048✔
723

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

726
    virtual void set_cluster(const Cluster*) {}
91,842✔
727

728
    // Recursively fetch tables of columns in expression tree. Used when user first builds a stand-alone expression
729
    // and
730
    // binds it to a Query at a later time
731
    virtual ConstTableRef get_base_table() const
732
    {
62,034✔
733
        return nullptr;
62,034✔
734
    }
62,034✔
735

736
    virtual void collect_dependencies(std::vector<TableKey>&) const {}
3,348✔
737

738
    virtual bool has_constant_evaluation() const
739
    {
161,982✔
740
        return false;
161,982✔
741
    }
161,982✔
742

743
    virtual bool has_single_value() const
744
    {
104,634✔
745
        return false;
104,634✔
746
    }
104,634✔
747

748
    virtual bool has_multiple_values() const
749
    {
×
750
        return false;
×
751
    }
×
752

753
    virtual bool has_search_index() const
754
    {
23,160✔
755
        return false;
23,160✔
756
    }
23,160✔
757

758
    virtual bool has_indexes_in_link_map() const
759
    {
1,344✔
760
        return false;
1,344✔
761
    }
1,344✔
762

763
    virtual std::vector<ObjKey> find_all(Mixed) const
764
    {
×
765
        return {};
×
766
    }
×
767

768
    virtual ConstTableRef get_target_table() const
769
    {
30,624✔
770
        return {};
30,624✔
771
    }
30,624✔
772

773
    virtual DataType get_type() const = 0;
774

775
    virtual void evaluate(Subexpr::Index& index, ValueBase& destination) = 0;
776

777
    virtual Mixed get_mixed() const
778
    {
×
779
        return {};
×
780
    }
×
781

782
    virtual util::Optional<ExpressionComparisonType> get_comparison_type() const
783
    {
979,425✔
784
        return util::none;
979,425✔
785
    }
979,425✔
786
};
787

788
template <typename T, typename... Args>
789
std::unique_ptr<Subexpr> make_subexpr(Args&&... args)
790
{
121,730✔
791
    return std::unique_ptr<Subexpr>(new T(std::forward<Args>(args)...));
121,730✔
792
}
121,730✔
793

794
template <class T>
795
class Columns;
796
template <class T>
797
class Value;
798
class ConstantMixedValue;
799
template <class T>
800
class Subexpr2;
801
template <class oper>
802
class Operator;
803
template <class oper, class TLeft = Subexpr>
804
class UnaryOperator;
805
template <class oper>
806
class SizeOperator;
807
template <class oper>
808
class TypeOfValueOperator;
809
template <class TCond>
810
class Compare;
811
template <bool has_links>
812
class UnaryLinkCompare;
813
class ColumnAccessorBase;
814

815

816
// Handle cases where left side is a constant (int, float, int64_t, double, StringData)
817
template <class Cond, class L, class R>
818
Query create(L left, const Subexpr2<R>& right)
819
{
32,230✔
820
    // Purpose of below code is to intercept the creation of a condition and test if it's supported by the old
821
    // query_engine.hpp which is faster. If it's supported, create a query_engine.hpp node, otherwise create a
822
    // query_expression.hpp node.
823
    //
824
    // This method intercepts only Value <cond> Subexpr2. Interception of Subexpr2 <cond> Subexpr is elsewhere.
825

826
    constexpr const bool supported_by_old_query_engine =
32,230✔
827
        (std::numeric_limits<L>::is_integer && std::numeric_limits<R>::is_integer) || std::is_same_v<R, Mixed> ||
32,230✔
828
        (std::is_same_v<L, R> &&
32,230✔
829
         realm::is_any_v<L, double, float, Timestamp, StringData, BinaryData, ObjectId, UUID>);
×
830

831
    if constexpr (REALM_OLDQUERY_FALLBACK && supported_by_old_query_engine) {
32,230✔
832
        const Columns<R>* column = dynamic_cast<const Columns<R>*>(&right);
28,830✔
833
        // TODO: recognize size operator expressions
834
        // auto size_operator = dynamic_cast<const SizeOperator<Size<StringData>, Subexpr>*>(&right);
835

836
        if (column && !column->links_exist() && !column->has_path()) {
28,830!
837
            ConstTableRef t = column->get_base_table();
21,646✔
838
            Query q(t);
21,646✔
839

840
            if constexpr (std::is_same_v<Cond, Less>)
10,823✔
841
                q.greater(column->column_key(), static_cast<R>(left));
1,058✔
842
            else if constexpr (std::is_same_v<Cond, Greater>)
10,294✔
843
                q.less(column->column_key(), static_cast<R>(left));
984✔
844
            else if constexpr (std::is_same_v<Cond, Equal>)
9,802✔
845
                q.equal(column->column_key(), static_cast<R>(left));
7,206✔
846
            else if constexpr (std::is_same_v<Cond, NotEqual>)
6,199✔
847
                q.not_equal(column->column_key(), static_cast<R>(left));
3,348✔
848
            else if constexpr (std::is_same_v<Cond, LessEqual>)
4,525✔
849
                q.greater_equal(column->column_key(), static_cast<R>(left));
962✔
850
            else if constexpr (std::is_same_v<Cond, GreaterEqual>)
4,044✔
851
                q.less_equal(column->column_key(), static_cast<R>(left));
966✔
852
            else if constexpr (std::is_same_v<Cond, EqualIns>)
3,561✔
853
                q.equal(column->column_key(), left, false);
714✔
854
            else if constexpr (std::is_same_v<Cond, NotEqualIns>)
3,204✔
855
                q.not_equal(column->column_key(), left, false);
702✔
856
            else if constexpr (std::is_same_v<Cond, BeginsWith>)
2,853✔
857
                q.begins_with(column->column_key(), left);
708✔
858
            else if constexpr (std::is_same_v<Cond, BeginsWithIns>)
2,499✔
859
                q.begins_with(column->column_key(), left, false);
708✔
860
            else if constexpr (std::is_same_v<Cond, EndsWith>)
2,145✔
861
                q.ends_with(column->column_key(), left);
708✔
862
            else if constexpr (std::is_same_v<Cond, EndsWithIns>)
1,791✔
863
                q.ends_with(column->column_key(), left, false);
708✔
864
            else if constexpr (std::is_same_v<Cond, Contains>)
1,437✔
865
                q.contains(column->column_key(), left);
726✔
866
            else if constexpr (std::is_same_v<Cond, ContainsIns>)
1,074✔
867
                q.contains(column->column_key(), left, false);
720✔
868
            else if constexpr (std::is_same_v<Cond, Like>)
714✔
869
                q.like(column->column_key(), left);
702✔
870
            else if constexpr (std::is_same_v<Cond, LikeIns>)
363✔
871
                q.like(column->column_key(), left, false);
726✔
872
            else {
10,823✔
873
                // query_engine.hpp does not support this Cond. Please either add support for it in query_engine.hpp
874
                // or fallback to using use 'return new Compare<>' instead.
875
                REALM_ASSERT(false);
10,823✔
876
            }
10,823✔
877
            return q;
21,646✔
878
        }
21,646✔
879
    }
28,830✔
880

881
    // Return query_expression.hpp node
882
    if constexpr (std::is_same_v<L, TypeOfValue>) {
7,422✔
883
        return make_expression<Compare<Cond>>(make_subexpr<Value<TypeOfValue>>(left), right.clone());
5,292✔
884
    }
5,281✔
885
    else {
21,385✔
886
        return make_expression<Compare<Cond>>(make_subexpr<ConstantMixedValue>(left), right.clone());
21,385✔
887
    }
21,385✔
888
}
19,707✔
889

890
// Purpose of this method is to intercept the creation of a condition and test if it's supported by the old
891
// query_engine.hpp which is faster. If it's supported, create a query_engine.hpp node, otherwise create a
892
// query_expression.hpp node.
893
//
894
// This method intercepts Subexpr2 <cond> Subexpr2 only. Value <cond> Subexpr2 is intercepted elsewhere.
895
template <class Cond, typename L, typename R>
896
Query create2(const Subexpr2<L>& left, const Subexpr2<R>& right)
897
{
3,800✔
898
#ifdef REALM_OLDQUERY_FALLBACK // if not defined, never fallback query_engine; always use query_expression
3,800✔
899
    // Test if expressions are of type Columns. Other possibilities are Value and Operator.
900
    const Columns<L>* left_col = dynamic_cast<const Columns<L>*>(&left);
3,800✔
901
    const Columns<R>* right_col = dynamic_cast<const Columns<R>*>(&right);
3,800✔
902

903
    // query_engine supports 'T-column <op> <T-column>' for T = {int64_t, float, double}, op = {<, >, ==, !=, <=,
904
    // >=},
905
    // but only if both columns are non-nullable, and aren't in linked tables.
906
    if (left_col && right_col) {
3,800✔
907
        ConstTableRef t = left_col->get_base_table();
3,688✔
908
        ConstTableRef t_right = right_col->get_base_table();
3,688✔
909
        REALM_ASSERT_DEBUG(t);
3,688✔
910
        REALM_ASSERT_DEBUG(t_right);
3,688✔
911
        // we only support multi column comparisons if they stem from the same table
912
        if (t->get_key() != t_right->get_key()) {
3,688✔
913
            throw Exception(
2✔
914
                ErrorCodes::InvalidQuery,
2✔
915
                util::format(
2✔
916
                    "Comparison between two properties must be linked with a relationship or exist on the same "
2✔
917
                    "Table (%1 and %2)",
2✔
918
                    t->get_name(), t_right->get_name()));
2✔
919
        }
2✔
920
        if (!left_col->links_exist() && !right_col->links_exist()) {
3,686✔
921
            if constexpr (std::is_same_v<Cond, Less>)
1,837✔
922
                return Query(t).less(left_col->column_key(), right_col->column_key());
390✔
923
            if constexpr (std::is_same_v<Cond, Greater>)
1,642✔
924
                return Query(t).greater(left_col->column_key(), right_col->column_key());
410✔
925
            if constexpr (std::is_same_v<Cond, Equal>)
1,437✔
926
                return Query(t).equal(left_col->column_key(), right_col->column_key());
1,166✔
927
            if constexpr (std::is_same_v<Cond, NotEqual>)
854✔
928
                return Query(t).not_equal(left_col->column_key(), right_col->column_key());
942✔
929
            if constexpr (std::is_same_v<Cond, LessEqual>)
383✔
930
                return Query(t).less_equal(left_col->column_key(), right_col->column_key());
384✔
931
            if constexpr (std::is_same_v<Cond, GreaterEqual>)
191✔
932
                return Query(t).greater_equal(left_col->column_key(), right_col->column_key());
382✔
933
        }
1,837✔
934
    }
3,686✔
935
#endif
1,961✔
936
    // Return query_expression.hpp node
937
    return make_expression<Compare<Cond>>(left.clone(), right.clone());
1,961✔
938
}
3,800✔
939

940
// All overloads where left-hand-side is Subexpr2<L>:
941
//
942
// left-hand-side       operator                              right-hand-side
943
// Subexpr2<L>          +, -, *, /, <, >, ==, !=, <=, >=      R, Subexpr2<R>
944
//
945
// For L = R = {int, int64_t, float, double, StringData, Timestamp}:
946
template <class L, class R>
947
class Overloads {
948
public:
949
    // Compare, right side constant
950
    friend Query operator>(const Subexpr2<L>& left, R right)
951
    {
5,014✔
952
        return create<Less>(right, left);
5,014✔
953
    }
5,014✔
954
    friend Query operator<(const Subexpr2<L>& left, R right)
955
    {
1,006✔
956
        return create<Greater>(right, left);
1,006✔
957
    }
1,006✔
958
    friend Query operator>=(const Subexpr2<L>& left, R right)
959
    {
980✔
960
        return create<LessEqual>(right, left);
980✔
961
    }
980✔
962
    friend Query operator<=(const Subexpr2<L>& left, R right)
963
    {
968✔
964
        return create<GreaterEqual>(right, left);
968✔
965
    }
968✔
966
    friend Query operator==(const Subexpr2<L>& left, R right)
967
    {
5,338✔
968
        return create<Equal>(right, left);
5,338✔
969
    }
5,338✔
970
    friend Query operator!=(const Subexpr2<L>& left, R right)
971
    {
2,806✔
972
        return create<NotEqual>(right, left);
2,806✔
973
    }
2,806✔
974

975
    // Compare left-side constant
976
    friend Query operator>(R left, const Subexpr2<L>& right)
977
    {
4✔
978
        return create<Greater>(left, right);
4✔
979
    }
4✔
980
    friend Query operator<(R left, const Subexpr2<L>& right)
981
    {
6✔
982
        return create<Less>(left, right);
6✔
983
    }
6✔
984
    friend Query operator>=(R left, const Subexpr2<L>& right)
985
    {
6✔
986
        return create<GreaterEqual>(left, right);
6✔
987
    }
6✔
988
    friend Query operator<=(R left, const Subexpr2<L>& right)
989
    {
6✔
990
        return create<LessEqual>(left, right);
6✔
991
    }
6✔
992
    friend Query operator==(R left, const Subexpr2<L>& right)
993
    {
8✔
994
        return create<Equal>(left, right);
8✔
995
    }
8✔
996
    friend Query operator!=(R left, const Subexpr2<L>& right)
997
    {
4✔
998
        return create<NotEqual>(left, right);
4✔
999
    }
4✔
1000

1001

1002
    // Compare, right side subexpression
1003
    friend Query operator==(const Subexpr2<L>& left, const Subexpr2<R>& right)
1004
    {
1,260✔
1005
        return create2<Equal>(left, right);
1,260✔
1006
    }
1,260✔
1007
    friend Query operator!=(const Subexpr2<L>& left, const Subexpr2<R>& right)
1008
    {
942✔
1009
        return create2<NotEqual>(left, right);
942✔
1010
    }
942✔
1011
    friend Query operator>(const Subexpr2<L>& left, const Subexpr2<R>& right)
1012
    {
422✔
1013
        return create2<Greater>(left, right);
422✔
1014
    }
422✔
1015
    friend Query operator<(const Subexpr2<L>& left, const Subexpr2<R>& right)
1016
    {
404✔
1017
        return create2<Less>(left, right);
404✔
1018
    }
404✔
1019
    friend Query operator>=(const Subexpr2<L>& left, const Subexpr2<R>& right)
1020
    {
382✔
1021
        return create2<GreaterEqual>(left, right);
382✔
1022
    }
382✔
1023
    friend Query operator<=(const Subexpr2<L>& left, const Subexpr2<R>& right)
1024
    {
390✔
1025
        return create2<LessEqual>(left, right);
390✔
1026
    }
390✔
1027
};
1028

1029
// With this wrapper class we can define just 20 overloads inside Overloads<L, R> instead of 5 * 20 = 100. Todo: We
1030
// can
1031
// consider if it's simpler/better to remove this class completely and just list all 100 overloads manually anyway.
1032
template <class T>
1033
class Subexpr2 : public Subexpr,
1034
                 public Overloads<T, int>,
1035
                 public Overloads<T, float>,
1036
                 public Overloads<T, double>,
1037
                 public Overloads<T, int64_t>,
1038
                 public Overloads<T, StringData>,
1039
                 public Overloads<T, bool>,
1040
                 public Overloads<T, Timestamp>,
1041
                 public Overloads<T, ObjectId>,
1042
                 public Overloads<T, Decimal128>,
1043
                 public Overloads<T, UUID>,
1044
                 public Overloads<T, Mixed>,
1045
                 public Overloads<T, null> {
1046
public:
1047
    DataType get_type() const override
1048
    {
1,866,237✔
1049
        return ColumnTypeTraits<T>::id;
1,866,237✔
1050
    }
1,866,237✔
1051
};
1052

1053
template <class Operator>
1054
Query compare(const Subexpr2<Link>& left, const Obj& obj);
1055
template <class Operator>
1056
Query compare(const Subexpr2<Link>& left, null obj);
1057

1058
// Subexpr2<Link> only provides equality comparisons. Their implementations can be found later in this file.
1059
template <>
1060
class Subexpr2<Link> : public Subexpr {
1061
public:
1062
    DataType get_type() const
1063
    {
×
1064
        return type_Link;
×
1065
    }
×
1066

1067
    friend Query operator==(const Subexpr2<Link>& left, const Obj& row)
1068
    {
174✔
1069
        return compare<Equal>(left, row);
174✔
1070
    }
174✔
1071
    friend Query operator!=(const Subexpr2<Link>& left, const Obj& row)
1072
    {
106✔
1073
        return compare<NotEqual>(left, row);
106✔
1074
    }
106✔
1075
    friend Query operator==(const Obj& row, const Subexpr2<Link>& right)
1076
    {
×
1077
        return compare<Equal>(right, row);
×
1078
    }
×
1079
    friend Query operator!=(const Obj& row, const Subexpr2<Link>& right)
1080
    {
×
1081
        return compare<NotEqual>(right, row);
×
1082
    }
×
1083

1084
    friend Query operator==(const Subexpr2<Link>& left, null)
1085
    {
6✔
1086
        return compare<Equal>(left, null());
6✔
1087
    }
6✔
1088
    friend Query operator!=(const Subexpr2<Link>& left, null)
1089
    {
36✔
1090
        return compare<NotEqual>(left, null());
36✔
1091
    }
36✔
1092
    friend Query operator==(null, const Subexpr2<Link>& right)
1093
    {
×
1094
        return compare<Equal>(right, null());
×
1095
    }
×
1096
    friend Query operator!=(null, const Subexpr2<Link>& right)
1097
    {
×
1098
        return compare<NotEqual>(right, null());
×
1099
    }
×
1100

1101
    friend Query operator==(const Subexpr2<Link>& left, const Subexpr2<Link>& right)
1102
    {
2✔
1103
        return make_expression<Compare<Equal>>(left.clone(), right.clone());
2✔
1104
    }
2✔
1105
    friend Query operator!=(const Subexpr2<Link>& left, const Subexpr2<Link>& right)
1106
    {
2✔
1107
        return make_expression<Compare<NotEqual>>(left.clone(), right.clone());
2✔
1108
    }
2✔
1109
};
1110

1111
template <>
1112
class Subexpr2<StringData> : public Subexpr, public Overloads<StringData, StringData> {
1113
public:
1114
    Query equal(StringData sd, bool case_sensitive = true);
1115
    Query equal(const Subexpr2<StringData>& col, bool case_sensitive = true);
1116
    Query not_equal(StringData sd, bool case_sensitive = true);
1117
    Query not_equal(const Subexpr2<StringData>& col, bool case_sensitive = true);
1118
    Query begins_with(StringData sd, bool case_sensitive = true);
1119
    Query begins_with(const Subexpr2<StringData>& col, bool case_sensitive = true);
1120
    Query ends_with(StringData sd, bool case_sensitive = true);
1121
    Query ends_with(const Subexpr2<StringData>& col, bool case_sensitive = true);
1122
    Query contains(StringData sd, bool case_sensitive = true);
1123
    Query contains(const Subexpr2<StringData>& col, bool case_sensitive = true);
1124
    Query like(StringData sd, bool case_sensitive = true);
1125
    Query like(const Subexpr2<StringData>& col, bool case_sensitive = true);
1126
    DataType get_type() const final
1127
    {
40,488✔
1128
        return type_String;
40,488✔
1129
    }
40,488✔
1130
};
1131

1132
template <>
1133
class Subexpr2<BinaryData> : public Subexpr, public Overloads<BinaryData, BinaryData> {
1134
public:
1135
    Query equal(BinaryData sd, bool case_sensitive = true);
1136
    Query equal(const Subexpr2<BinaryData>& col, bool case_sensitive = true);
1137
    Query not_equal(BinaryData sd, bool case_sensitive = true);
1138
    Query not_equal(const Subexpr2<BinaryData>& col, bool case_sensitive = true);
1139
    Query begins_with(BinaryData sd, bool case_sensitive = true);
1140
    Query begins_with(const Subexpr2<BinaryData>& col, bool case_sensitive = true);
1141
    Query ends_with(BinaryData sd, bool case_sensitive = true);
1142
    Query ends_with(const Subexpr2<BinaryData>& col, bool case_sensitive = true);
1143
    Query contains(BinaryData sd, bool case_sensitive = true);
1144
    Query contains(const Subexpr2<BinaryData>& col, bool case_sensitive = true);
1145
    Query like(BinaryData sd, bool case_sensitive = true);
1146
    Query like(const Subexpr2<BinaryData>& col, bool case_sensitive = true);
1147
    DataType get_type() const final
1148
    {
8,196✔
1149
        return type_Binary;
8,196✔
1150
    }
8,196✔
1151
};
1152

1153
template <>
1154
class Subexpr2<Mixed> : public Subexpr,
1155
                        public Overloads<Mixed, Mixed>,
1156
                        public Overloads<Mixed, int>,
1157
                        public Overloads<Mixed, float>,
1158
                        public Overloads<Mixed, double>,
1159
                        public Overloads<Mixed, int64_t>,
1160
                        public Overloads<Mixed, StringData>,
1161
                        public Overloads<Mixed, bool>,
1162
                        public Overloads<Mixed, Timestamp>,
1163
                        public Overloads<Mixed, ObjectId>,
1164
                        public Overloads<Mixed, Decimal128>,
1165
                        public Overloads<Mixed, UUID>,
1166
                        public Overloads<Mixed, null> {
1167
public:
1168
    Query equal(Mixed sd, bool case_sensitive = true);
1169
    Query equal(const Subexpr2<Mixed>& col, bool case_sensitive = true);
1170
    Query not_equal(Mixed sd, bool case_sensitive = true);
1171
    Query not_equal(const Subexpr2<Mixed>& col, bool case_sensitive = true);
1172
    Query begins_with(Mixed sd, bool case_sensitive = true);
1173
    Query begins_with(const Subexpr2<Mixed>& col, bool case_sensitive = true);
1174
    Query ends_with(Mixed sd, bool case_sensitive = true);
1175
    Query ends_with(const Subexpr2<Mixed>& col, bool case_sensitive = true);
1176
    Query contains(Mixed sd, bool case_sensitive = true);
1177
    Query contains(const Subexpr2<Mixed>& col, bool case_sensitive = true);
1178
    Query like(Mixed sd, bool case_sensitive = true);
1179
    Query like(const Subexpr2<Mixed>& col, bool case_sensitive = true);
1180
    DataType get_type() const override
1181
    {
9,756✔
1182
        return type_Mixed;
9,756✔
1183
    }
9,756✔
1184

1185
    using T = Mixed; // used inside the following macros for operator overloads
1186
};
1187

1188
template <>
1189
class Subexpr2<TypeOfValue> : public Subexpr, public Overloads<TypeOfValue, TypeOfValue> {
1190
public:
1191
    Query equal(TypeOfValue v);
1192
    Query equal(const TypeOfValueOperator<Mixed>& col);
1193
    Query not_equal(TypeOfValue v);
1194
    Query not_equal(const TypeOfValueOperator<Mixed>& col);
1195
    DataType get_type() const final
1196
    {
2,056✔
1197
        return type_TypeOfValue;
2,056✔
1198
    }
2,056✔
1199
};
1200

1201
#if REALM_ENABLE_GEOSPATIAL
1202
template <>
1203
class Subexpr2<Geospatial> : public Subexpr, public Overloads<Geospatial, Geospatial> {
1204
public:
1205
    DataType get_type() const final
1206
    {
×
1207
        return type_Geospatial;
×
1208
    }
×
1209
};
1210
#endif
1211

1212
struct TrueExpression : Expression {
1213
    size_t find_first(size_t start, size_t end) const override
1214
    {
1,096✔
1215
        REALM_ASSERT(start <= end);
1,096✔
1216
        if (start != end)
1,096✔
1217
            return start;
1,092✔
1218

1219
        return realm::not_found;
4✔
1220
    }
1,096✔
1221
    void set_base_table(ConstTableRef) override {}
460✔
1222
    void set_cluster(const Cluster*) override {}
680✔
1223
    ConstTableRef get_base_table() const override
1224
    {
460✔
1225
        return nullptr;
460✔
1226
    }
460✔
1227
    std::string description(util::serializer::SerialisationState&) const override
1228
    {
220✔
1229
        return "TRUEPREDICATE";
220✔
1230
    }
220✔
1231
    std::unique_ptr<Expression> clone() const override
1232
    {
736✔
1233
        return std::unique_ptr<Expression>(new TrueExpression(*this));
736✔
1234
    }
736✔
1235
};
1236

1237

1238
struct FalseExpression : Expression {
1239
    size_t find_first(size_t, size_t) const override
1240
    {
1,164✔
1241
        return realm::not_found;
1,164✔
1242
    }
1,164✔
1243
    void set_base_table(ConstTableRef) override {}
558✔
1244
    void set_cluster(const Cluster*) override {}
1,092✔
1245
    std::string description(util::serializer::SerialisationState&) const override
1246
    {
156✔
1247
        return "FALSEPREDICATE";
156✔
1248
    }
156✔
1249
    ConstTableRef get_base_table() const override
1250
    {
228✔
1251
        return nullptr;
228✔
1252
    }
228✔
1253
    std::unique_ptr<Expression> clone() const override
1254
    {
582✔
1255
        return std::unique_ptr<Expression>(new FalseExpression(*this));
582✔
1256
    }
582✔
1257
};
1258

1259

1260
// Stores N values of type T. Can also exchange data with other ValueBase of different types
1261
template <class T>
1262
class Value : public ValueBase, public Subexpr2<T> {
1263
public:
1264
    Value() = default;
7,377,651✔
1265

1266
    Value(T init)
1267
        : ValueBase(QueryValue(init))
265,641✔
1268
    {
531,279✔
1269
    }
531,279✔
1270

1271
    std::string value_to_string(size_t ndx, util::serializer::SerialisationState& state) const
1272
    {
17,374✔
1273
        auto val = get(ndx);
17,374✔
1274
        if (val.is_null())
17,374✔
1275
            return "NULL";
982✔
1276
        else {
16,392✔
1277
            static_cast<void>(state);
16,392✔
1278
            if constexpr (std::is_same_v<T, TypeOfValue>) {
14,606✔
1279
                return util::serializer::print_value(val.get_type_of_value());
8,196✔
1280
            }
7,994✔
1281
            else if constexpr (std::is_same_v<T, ObjKey>) {
8,014✔
1282
                ObjKey obj_key = val.template get<ObjKey>();
7,994✔
1283
                if (state.target_table) {
7,994✔
1284
                    ObjLink link(state.target_table->get_key(), obj_key);
1,826✔
1285
                    return util::serializer::print_value(link, state.group);
1,826✔
1286
                }
1,826✔
1287
                else {
1,786✔
1288
                    return util::serializer::print_value(obj_key);
1,786✔
1289
                }
1,786✔
1290
            }
1,806✔
1291
            else if constexpr (std::is_same_v<T, ObjLink>) {
7,976✔
1292
                return util::serializer::print_value(val.template get<ObjLink>(), state.group);
7,974✔
1293
            }
7,972✔
1294
            else if constexpr (std::is_same_v<T, Mixed>) {
9,472✔
1295
                if (val.is_type(type_TypedLink)) {
7,972✔
1296
                    return util::serializer::print_value(val.template get<ObjLink>(), state.group);
6✔
1297
                }
6✔
1298
                else {
2,994✔
1299
                    return util::serializer::print_value(val);
2,994✔
1300
                }
2,994✔
1301
            }
1,500✔
1302
            else {
12,944✔
1303
                return util::serializer::print_value(val.template get<T>());
12,944✔
1304
            }
12,944✔
1305
        }
16,392✔
1306
    }
17,374✔
1307

1308
    std::string description(util::serializer::SerialisationState& state) const override
1309
    {
15,948✔
1310
        const size_t sz = size();
15,948✔
1311
        if (m_from_list) {
15,948✔
1312
            std::string desc = state.describe_expression_type(m_comparison_type) + "{";
1,574✔
1313
            for (size_t i = 0; i < sz; ++i) {
4,574!
1314
                if (i != 0) {
3,000!
1315
                    desc += ", ";
1,750✔
1316
                }
1,750✔
1317
                desc += value_to_string(i, state);
3,000✔
1318
            }
3,000✔
1319
            desc += "}";
1,574✔
1320
            return desc;
1,574✔
1321
        }
1,574✔
1322
        else if (sz == 1) {
14,374✔
1323
            return value_to_string(0, state);
14,374✔
1324
        }
14,374✔
1325
        return "";
×
1326
    }
15,948✔
1327

1328
    bool has_multiple_values() const override
1329
    {
914✔
1330
        return m_from_list;
914✔
1331
    }
914✔
1332

1333
    bool has_single_value() const override
1334
    {
560,149✔
1335
        return size() == 1;
560,149✔
1336
    }
560,149✔
1337

1338
    bool has_constant_evaluation() const override
1339
    {
105,066✔
1340
        return true;
105,066✔
1341
    }
105,066✔
1342

1343
    util::Optional<ExpressionComparisonType> get_comparison_type() const final
1344
    {
1,479,141✔
1345
        REALM_ASSERT_DEBUG(!m_comparison_type || m_from_list);
1,479,141!
1346
        return m_comparison_type;
1,479,141✔
1347
    }
1,479,141✔
1348

1349
    void set_comparison_type(util::Optional<ExpressionComparisonType> type)
1350
    {
2,868✔
1351
        m_comparison_type = type;
2,868✔
1352
    }
2,868✔
1353

1354
    Mixed get_mixed() const override
1355
    {
511,479✔
1356
        return get(0);
511,479✔
1357
    }
511,479✔
1358

1359
    void evaluate(Subexpr::Index&, ValueBase& destination) override
1360
    {
120✔
1361
        destination = *this;
120✔
1362
    }
120✔
1363

1364
    std::unique_ptr<Subexpr> clone() const override
1365
    {
26,120✔
1366
        return make_subexpr<Value<T>>(*this);
26,120✔
1367
    }
26,120✔
1368

1369
protected:
1370
    util::Optional<ExpressionComparisonType> m_comparison_type;
1371
};
1372

1373
class ConstantMixedValue : public Value<Mixed> {
1374
public:
1375
    ConstantMixedValue(const Mixed& val)
1376
        : Value(val)
11,457✔
1377
    {
22,911✔
1378
        begin()->use_buffer(m_buffer);
22,911✔
1379
    }
22,911✔
1380

1381
    std::unique_ptr<Subexpr> clone() const override
1382
    {
15,798✔
1383
        return std::unique_ptr<Subexpr>(new ConstantMixedValue(*this));
15,798✔
1384
    }
15,798✔
1385

1386
private:
1387
    ConstantMixedValue(const ConstantMixedValue& other)
1388
        : Value(other)
7,899✔
1389
    {
15,798✔
1390
        begin()->use_buffer(m_buffer);
15,798✔
1391
    }
15,798✔
1392

1393
    std::string m_buffer;
1394
};
1395

1396
class ConstantMixedList : public Value<Mixed> {
1397
public:
1398
    ConstantMixedList(size_t nb_values)
1399
        : Value()
1,310✔
1400
        , m_buffer(nb_values)
1,310✔
1401
    {
2,596✔
1402
        this->init(true, nb_values);
2,596✔
1403
    }
2,596✔
1404
    void set(size_t n, Mixed val)
1405
    {
5,180✔
1406
        Value<Mixed>::set(n, val);
5,180✔
1407
        (*this)[n].use_buffer(m_buffer[n]);
5,180✔
1408
    }
5,180✔
1409

1410
    std::unique_ptr<Subexpr> clone() const override
1411
    {
2,356✔
1412
        return std::unique_ptr<Subexpr>(new ConstantMixedList(*this));
2,356✔
1413
    }
2,356✔
1414

1415
private:
1416
    ConstantMixedList(const ConstantMixedList& other)
1417
        : Value(other)
1,178✔
1418
        , m_buffer(other.size())
1,178✔
1419
    {
2,356✔
1420
        for (size_t i = 0; i < size(); i++) {
7,020✔
1421
            (*this)[i].use_buffer(m_buffer[i]);
4,664✔
1422
        }
4,664✔
1423
    }
2,356✔
1424

1425
    std::vector<std::string> m_buffer;
1426
};
1427

1428
class ConstantStringValue : public Value<StringData> {
1429
public:
1430
    ConstantStringValue(const StringData& string)
1431
        : Value()
4,282✔
1432
        , m_string(string.is_null() ? util::none : util::make_optional(std::string(string)))
4,282✔
1433
    {
8,564✔
1434
        if (m_string)
8,564✔
1435
            set(0, *m_string);
8,216✔
1436
    }
8,564✔
1437

1438
    std::unique_ptr<Subexpr> clone() const override
1439
    {
5,224✔
1440
        return std::unique_ptr<Subexpr>(new ConstantStringValue(*this));
5,224✔
1441
    }
5,224✔
1442

1443
private:
1444
    ConstantStringValue(const ConstantStringValue& other)
1445
        : Value()
2,612✔
1446
        , m_string(other.m_string)
2,612✔
1447
    {
5,224✔
1448
        if (m_string)
5,224✔
1449
            set(0, *m_string);
5,088✔
1450
    }
5,224✔
1451

1452
    util::Optional<std::string> m_string;
1453
};
1454

1455
class ConstantBinaryValue : public Value<BinaryData> {
1456
public:
1457
    ConstantBinaryValue(const BinaryData& bin)
1458
        : Value()
968✔
1459
        , m_buffer(bin)
968✔
1460
    {
1,936✔
1461
        if (m_buffer.data())
1,936✔
1462
            set(0, BinaryData(m_buffer.data(), m_buffer.size()));
1,936✔
1463
    }
1,936✔
1464

1465
    std::unique_ptr<Subexpr> clone() const override
1466
    {
352✔
1467
        return std::unique_ptr<Subexpr>(new ConstantBinaryValue(*this));
352✔
1468
    }
352✔
1469

1470
private:
1471
    ConstantBinaryValue(const ConstantBinaryValue& other)
1472
        : Value()
176✔
1473
        , m_buffer(other.m_buffer)
176✔
1474
    {
352✔
1475
        if (m_buffer.data())
352✔
1476
            set(0, BinaryData(m_buffer.data(), m_buffer.size()));
352✔
1477
    }
352✔
1478

1479
    OwnedBinaryData m_buffer;
1480
};
1481

1482
#if REALM_ENABLE_GEOSPATIAL
1483
class ConstantGeospatialValue : public Value<Geospatial> {
1484
public:
1485
    ConstantGeospatialValue(const Geospatial& geo)
1486
        : Value()
62✔
1487
        , m_geospatial(geo)
62✔
1488
    {
124✔
1489
        if (geo.get_type() != Geospatial::Type::Invalid) {
124✔
1490
            set(0, Mixed{&m_geospatial});
124✔
1491
        }
124✔
1492
    }
124✔
1493

1494
    std::unique_ptr<Subexpr> clone() const override
1495
    {
×
1496
        return std::unique_ptr<Subexpr>(new ConstantGeospatialValue(*this));
×
1497
    }
×
1498

1499
private:
1500
    ConstantGeospatialValue(const ConstantGeospatialValue& other)
1501
        : Value()
1502
        , m_geospatial(other.m_geospatial)
1503
    {
×
1504
        if (m_geospatial.get_type() != Geospatial::Type::Invalid) {
×
1505
            set(0, Mixed{&m_geospatial});
×
1506
        }
×
1507
    }
×
1508
    Geospatial m_geospatial;
1509
};
1510
#endif
1511

1512
// Classes used for LinkMap (see below).
1513

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

1518
using LinkMapFunction = util::FunctionRef<bool(ObjKey)>;
1519

1520
/*
1521
The LinkMap and LinkMapFunction classes are used for query conditions on links themselves (contrary to conditions on
1522
the value payload they point at).
1523

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

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

1531
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
1532
found the first link that points to key '5'. Other solutions could be a std::vector<ColKey> harvest_all_links(), or an
1533
iterator pattern. First solution can't exit, second solution requires internal state.
1534
*/
1535
class LinkMap final {
1536
public:
1537
    LinkMap() = default;
330✔
1538
    LinkMap(ConstTableRef table, std::vector<ExtendedColumnKey> columns)
1539
        : m_link_column_keys(std::move(columns))
271,594✔
1540
    {
543,188✔
1541
        set_base_table(table);
543,188✔
1542
    }
543,188✔
1543

1544
    LinkMap(LinkMap const& other)
1545
    {
189,708✔
1546
        m_link_column_keys = other.m_link_column_keys;
189,708✔
1547
        m_tables = other.m_tables;
189,708✔
1548
        m_link_types = other.m_link_types;
189,708✔
1549
        m_only_unary_links = other.m_only_unary_links;
189,708✔
1550
    }
189,708✔
1551

1552
    size_t get_nb_hops() const
1553
    {
2,048✔
1554
        return m_link_column_keys.size();
2,048✔
1555
    }
2,048✔
1556

1557
    bool has_links() const
1558
    {
7,896,564✔
1559
        return m_link_column_keys.size() > 0;
7,896,564✔
1560
    }
7,896,564✔
1561
    bool has_indexes() const
1562
    {
1,024✔
1563
        for (auto& k : m_link_column_keys) {
1,216✔
1564
            if (k.has_index())
1,216✔
1565
                return true;
64✔
1566
        }
1,216✔
1567
        return false;
960✔
1568
    }
1,024✔
1569

1570
    ColKey get_first_column_key() const
1571
    {
304✔
1572
        REALM_ASSERT(has_links());
304✔
1573
        return m_link_column_keys[0];
304✔
1574
    }
304✔
1575

1576
    void set_base_table(ConstTableRef table);
1577

1578
    void set_cluster(const Cluster* cluster)
1579
    {
30,894✔
1580
        Allocator& alloc = get_base_table()->get_alloc();
30,894✔
1581
        ArrayPayload* array_ptr;
30,894✔
1582
        switch (m_link_types[0]) {
30,894✔
1583
            case col_type_Link:
30,000✔
1584
                if (m_link_column_keys[0].is_list()) {
30,000✔
1585
                    array_ptr = &m_leaf.emplace<ArrayList>(alloc);
14,052✔
1586
                }
14,052✔
1587
                else if (m_link_column_keys[0].is_dictionary()) {
15,948✔
1588
                    array_ptr = &m_leaf.emplace<ArrayInteger>(alloc);
324✔
1589
                }
324✔
1590
                else {
15,624✔
1591
                    array_ptr = &m_leaf.emplace<ArrayKey>(alloc);
15,624✔
1592
                }
15,624✔
1593
                break;
30,000✔
1594
            case col_type_BackLink:
894✔
1595
                array_ptr = &m_leaf.emplace<ArrayBacklink>(alloc);
894✔
1596
                break;
894✔
1597
            default:
✔
1598
                REALM_UNREACHABLE();
1599
        }
30,894✔
1600
        cluster->init_leaf(m_link_column_keys[0], array_ptr);
30,894✔
1601
    }
30,894✔
1602

1603
    void collect_dependencies(std::vector<TableKey>& tables) const;
1604

1605
    std::string description(util::serializer::SerialisationState& state) const;
1606

1607
    ObjKey get_unary_link_or_not_found(size_t index) const
1608
    {
55,324✔
1609
        REALM_ASSERT(m_only_unary_links);
55,324✔
1610
        ObjKey result;
55,324✔
1611
        map_links(index, [&](ObjKey key) {
55,324✔
1612
            result = key;
53,924✔
1613
            return false; // exit search, only one result ever expected
53,924✔
1614
        });
53,924✔
1615
        return result;
55,324✔
1616
    }
55,324✔
1617

1618
    std::vector<ObjKey> get_links(size_t index) const
1619
    {
247,887✔
1620
        std::vector<ObjKey> res;
247,887✔
1621
        get_links(index, res);
247,887✔
1622
        return res;
247,887✔
1623
    }
247,887✔
1624

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

1627
    size_t count_links(size_t row) const
1628
    {
22,830✔
1629
        size_t count = 0;
22,830✔
1630
        map_links(row, [&](ObjKey) {
22,830✔
1631
            ++count;
17,004✔
1632
            return true;
17,004✔
1633
        });
17,004✔
1634
        return count;
22,830✔
1635
    }
22,830✔
1636

1637
    size_t count_all_backlinks(size_t row) const
1638
    {
184✔
1639
        size_t count = 0;
184✔
1640
        auto table = get_target_table().unchecked_ptr();
184✔
1641
        map_links(row, [&](ObjKey key) {
432✔
1642
            count += table->get_object(key).get_backlink_count();
432✔
1643
            return true;
432✔
1644
        });
432✔
1645
        return count;
184✔
1646
    }
184✔
1647

1648
    void map_links(size_t row, LinkMapFunction lm) const
1649
    {
366,321✔
1650
        map_links(0, row, lm);
366,321✔
1651
    }
366,321✔
1652

1653
    bool only_unary_links() const
1654
    {
311,151✔
1655
        return m_only_unary_links;
311,151✔
1656
    }
311,151✔
1657

1658
    ConstTableRef get_base_table() const
1659
    {
1,392,126✔
1660
        return m_tables.empty() ? nullptr : m_tables[0];
1,392,126✔
1661
    }
1,392,126✔
1662

1663
    ConstTableRef get_target_table() const
1664
    {
2,576,961✔
1665
        REALM_ASSERT(!m_tables.empty());
2,576,961✔
1666
        return m_tables.back();
2,576,961✔
1667
    }
2,576,961✔
1668

1669
    bool links_exist() const
1670
    {
23,808✔
1671
        return !m_link_column_keys.empty();
23,808✔
1672
    }
23,808✔
1673

1674
    ColKey pop_last()
1675
    {
44✔
1676
        ColKey col = m_link_column_keys.back();
44✔
1677
        m_link_column_keys.pop_back();
44✔
1678
        m_tables.pop_back();
44✔
1679
        return col;
44✔
1680
    }
44✔
1681

1682
private:
1683
    bool map_links(size_t column, ObjKey key, LinkMapFunction lm) const;
1684
    void map_links(size_t column, size_t row, LinkMapFunction lm) const;
1685

1686
    void get_links(size_t row, std::vector<ObjKey>& result) const
1687
    {
247,890✔
1688
        map_links(row, [&](ObjKey key) {
357,966✔
1689
            result.push_back(key);
357,966✔
1690
            return true; // continue evaluation
357,966✔
1691
        });
357,966✔
1692
    }
247,890✔
1693

1694
    mutable std::vector<ExtendedColumnKey> m_link_column_keys;
1695
    std::vector<ColumnType> m_link_types;
1696
    std::vector<ConstTableRef> m_tables;
1697
    bool m_only_unary_links = true;
1698

1699
    mpark::variant<mpark::monostate, ArrayKey, ArrayInteger, ArrayList, ArrayBacklink> m_leaf;
1700

1701
    template <class>
1702
    friend Query compare(const Subexpr2<Link>&, const Obj&);
1703
};
1704

1705
template <class T>
1706
Value<T> make_value_for_link(bool only_unary_links, size_t size)
1707
{
1708
    Value<T> value;
1709
    if (only_unary_links) {
1710
        REALM_ASSERT(size <= 1);
1711
        value.init(false, 1);
1712
        value.m_storage.set_null(0);
1713
    }
1714
    else {
1715
        value.init(true, size);
1716
    }
1717
    return value;
1718
}
1719

1720
// This class can be used as untyped base for expressions that handle object properties
1721
class ObjPropertyBase {
1722
public:
1723
    ObjPropertyBase(ColKey column, ConstTableRef table, std::vector<ExtendedColumnKey> links,
1724
                    util::Optional<ExpressionComparisonType> type)
1725
        : m_link_map(table, std::move(links))
255,400✔
1726
        , m_column_key(column)
255,400✔
1727
        , m_comparison_type(type)
255,400✔
1728
    {
510,798✔
1729
    }
510,798✔
1730
    ObjPropertyBase(const ObjPropertyBase& other)
1731
        : m_link_map(other.m_link_map)
28,730✔
1732
        , m_column_key(other.m_column_key)
28,730✔
1733
        , m_comparison_type(other.m_comparison_type)
28,730✔
1734
    {
57,460✔
1735
    }
57,460✔
1736
    ObjPropertyBase(ColKey column, const LinkMap& link_map, util::Optional<ExpressionComparisonType> type)
1737
        : m_link_map(link_map)
1738
        , m_column_key(column)
1739
        , m_comparison_type(type)
1740
    {
×
1741
    }
×
1742

1743
    bool links_exist() const
1744
    {
5,698,716✔
1745
        return m_link_map.has_links();
5,698,716✔
1746
    }
5,698,716✔
1747

1748
    bool only_unary_links() const
1749
    {
×
1750
        return m_link_map.only_unary_links();
×
1751
    }
×
1752

1753
    bool is_nullable() const
1754
    {
2,998,380✔
1755
        return m_column_key.get_attrs().test(col_attr_Nullable);
2,998,380✔
1756
    }
2,998,380✔
1757

1758
    const LinkMap& get_link_map() const
1759
    {
2,430✔
1760
        return m_link_map;
2,430✔
1761
    }
2,430✔
1762

1763
    ColKey column_key() const noexcept
1764
    {
728,535✔
1765
        return m_column_key;
728,535✔
1766
    }
728,535✔
1767

1768
    virtual bool has_path() const noexcept
1769
    {
469,920✔
1770
        return false;
469,920✔
1771
    }
469,920✔
1772

1773
protected:
1774
    LinkMap m_link_map;
1775
    // Column index of payload column of m_table
1776
    mutable ColKey m_column_key;
1777
    util::Optional<ExpressionComparisonType> m_comparison_type; // Any, All, None
1778
};
1779

1780
// Combines Subexpr2<T> and ObjPropertyBase
1781
// Implements virtual functions defined in Expression/Subexpr
1782
template <class T>
1783
class ObjPropertyExpr : public Subexpr2<T>, public ObjPropertyBase {
1784
public:
1785
    using ObjPropertyBase::ObjPropertyBase;
1786

1787
    bool has_multiple_values() const override
1788
    {
184✔
1789
        return m_link_map.has_links() && !m_link_map.only_unary_links();
184!
1790
    }
184✔
1791

1792
    ConstTableRef get_base_table() const final
1793
    {
169,176✔
1794
        return m_link_map.get_base_table();
169,176✔
1795
    }
169,176✔
1796

1797
    void set_base_table(ConstTableRef table) override
1798
    {
29,680✔
1799
        if (table != get_base_table()) {
29,680✔
1800
            m_link_map.set_base_table(table);
3,152✔
1801
        }
3,152✔
1802
    }
29,680✔
1803

1804
    bool has_search_index() const final
1805
    {
7,836✔
1806
        auto target_table = m_link_map.get_target_table();
7,836✔
1807
        return target_table->search_index_type(m_column_key) == IndexType::General;
7,836✔
1808
    }
7,836✔
1809

1810
    bool has_indexes_in_link_map() const final
1811
    {
1,024✔
1812
        return m_link_map.has_indexes();
1,024✔
1813
    }
1,024✔
1814

1815
    std::vector<ObjKey> find_all(Mixed value) const final
1816
    {
704✔
1817
        std::vector<ObjKey> ret;
704✔
1818
        std::vector<ObjKey> result;
704✔
1819

1820
        if (value.is_null() && !m_column_key.is_nullable()) {
704!
1821
            return ret;
20✔
1822
        }
20✔
1823

1824
        if (m_link_map.get_target_table()->get_primary_key_column() == m_column_key) {
684!
1825
            // Only one object with a given key would be possible
1826
            if (auto k = m_link_map.get_target_table()->find_primary_key(value))
8!
1827
                result.push_back(k);
8✔
1828
        }
8✔
1829
        else {
676✔
1830
            SearchIndex* index = m_link_map.get_target_table()->get_search_index(m_column_key);
676✔
1831
            REALM_ASSERT(index);
676!
1832
            if (value.is_null()) {
676!
1833
                index->find_all(result, realm::null{});
24✔
1834
            }
24✔
1835
            else {
652✔
1836
                T val = value.get<T>();
652✔
1837
                index->find_all(result, val);
652✔
1838
            }
652✔
1839
        }
676✔
1840

1841
        for (ObjKey k : result) {
10,124!
1842
            auto ndxs = m_link_map.get_origin_objkeys(k);
10,124✔
1843
            ret.insert(ret.end(), ndxs.begin(), ndxs.end());
10,124✔
1844
        }
10,124✔
1845

1846
        return ret;
684✔
1847
    }
704✔
1848

1849
    void collect_dependencies(std::vector<TableKey>& tables) const final
1850
    {
8,692✔
1851
        m_link_map.collect_dependencies(tables);
8,692✔
1852
    }
8,692✔
1853

1854
    std::string description(util::serializer::SerialisationState& state) const override
1855
    {
2,740✔
1856
        return state.describe_expression_type(m_comparison_type) + state.describe_columns(m_link_map, m_column_key);
2,740✔
1857
    }
2,740✔
1858

1859
    util::Optional<ExpressionComparisonType> get_comparison_type() const final
1860
    {
770,874✔
1861
        return m_comparison_type;
770,874✔
1862
    }
770,874✔
1863

1864
    std::unique_ptr<Subexpr> clone() const override
1865
    {
44,796✔
1866
        return make_subexpr<Columns<T>>(static_cast<const Columns<T>&>(*this));
44,796✔
1867
    }
44,796✔
1868
};
1869

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

1877
template <class T>
1878
class SimpleQuerySupport : public ObjPropertyExpr<T> {
1879
public:
1880
    using ObjPropertyExpr<T>::links_exist;
1881

1882
    SimpleQuerySupport(ColKey column, ConstTableRef table, const std::vector<ExtendedColumnKey>& links = {},
1883
                       util::Optional<ExpressionComparisonType> type = util::none)
1884
        : ObjPropertyExpr<T>(column, table, links, type)
232,428✔
1885
    {
464,864✔
1886
    }
464,864✔
1887

1888
    void set_cluster(const Cluster* cluster) override
1889
    {
19,540✔
1890
        if (links_exist()) {
19,540✔
1891
            m_link_map.set_cluster(cluster);
4,360✔
1892
        }
4,360✔
1893
        else {
15,180✔
1894
            m_leaf.emplace(m_link_map.get_base_table()->get_alloc());
15,180✔
1895
            cluster->init_leaf(m_column_key, &*m_leaf);
15,180✔
1896
        }
15,180✔
1897
    }
19,540✔
1898

1899
    void evaluate(Subexpr::Index& index, ValueBase& destination) override
1900
    {
314,764✔
1901
        if (links_exist()) {
314,764✔
1902
            REALM_ASSERT(!m_leaf);
40,672✔
1903

1904
            if (m_link_map.only_unary_links()) {
40,672✔
1905
                REALM_ASSERT(destination.size() == 1);
35,108✔
1906
                REALM_ASSERT(!destination.m_from_list);
35,108✔
1907
                destination.set_null(0);
35,108✔
1908
                auto link_translation_key = this->m_link_map.get_unary_link_or_not_found(index);
35,108✔
1909
                if (link_translation_key) {
35,108✔
1910
                    const Obj obj = m_link_map.get_target_table()->get_object(link_translation_key);
34,172✔
1911
                    if constexpr (realm::is_any_v<T, ObjectId, UUID>) {
34,172✔
1912
                        auto opt_val = obj.get<util::Optional<T>>(m_column_key);
17,086✔
1913
                        if (opt_val) {
18,048✔
1914
                            destination.set(0, *opt_val);
2,208✔
1915
                        }
2,208✔
1916
                        else {
15,840✔
1917
                            destination.set_null(0);
15,840✔
1918
                        }
15,840✔
1919
                    }
9,024✔
1920
                    else {
16,124✔
1921
                        destination.set(0, obj.get<T>(m_column_key));
16,124✔
1922
                    }
16,124✔
1923
                }
34,172✔
1924
            }
35,108✔
1925
            else {
5,564✔
1926
                std::vector<ObjKey> links = m_link_map.get_links(index);
5,564✔
1927
                destination.init(true, links.size());
5,564✔
1928
                for (size_t t = 0; t < links.size(); t++) {
42,624!
1929
                    const Obj obj = m_link_map.get_target_table()->get_object(links[t]);
37,060✔
1930
                    if constexpr (realm::is_any_v<T, ObjectId, UUID>) {
37,060✔
1931
                        auto opt_val = obj.get<util::Optional<T>>(m_column_key);
18,530✔
1932
                        if (opt_val) {
18,530✔
1933
                            destination.set(t, *opt_val);
408✔
1934
                        }
408✔
1935
                        else {
11,592✔
1936
                            destination.set_null(t);
11,592✔
1937
                        }
11,592✔
1938
                    }
6,000✔
1939
                    else {
25,060✔
1940
                        destination.set(t, obj.get<T>(m_column_key));
25,060✔
1941
                    }
25,060✔
1942
                }
37,060✔
1943
            }
5,564✔
1944
        }
40,672✔
1945
        else {
274,092✔
1946
            // Not a link column
1947
            REALM_ASSERT(m_leaf);
274,092✔
1948
            REALM_ASSERT(destination.size() == 1);
274,092✔
1949
            REALM_ASSERT(!destination.m_from_list);
274,092✔
1950
            if (m_leaf->is_null(index)) {
274,092✔
1951
                destination.set_null(0);
59,888✔
1952
            }
59,888✔
1953
            else {
214,204✔
1954
                destination.set(0, m_leaf->get(index));
214,204✔
1955
            }
214,204✔
1956
        }
274,092✔
1957
    }
314,764✔
1958

1959
    void evaluate(ObjKey key, ValueBase& destination)
1960
    {
3,408✔
1961
        Value<T>& d = static_cast<Value<T>&>(destination);
3,408✔
1962
        d.set(0, m_link_map.get_target_table()->get_object(key).template get<T>(m_column_key));
3,408✔
1963
    }
3,408✔
1964

1965
    SimpleQuerySupport(const SimpleQuerySupport& other)
1966
        : ObjPropertyExpr<T>(other)
11,466✔
1967
    {
22,932✔
1968
    }
22,932✔
1969

1970
    SizeOperator<T> size()
1971
    {
780✔
1972
        return SizeOperator<T>(this->clone());
780✔
1973
    }
780✔
1974

1975
    TypeOfValueOperator<T> type_of_value()
1976
    {
664✔
1977
        return TypeOfValueOperator<T>(this->clone());
664✔
1978
    }
664✔
1979

1980
protected:
1981
    using ObjPropertyExpr<T>::m_link_map;
1982
    using ObjPropertyExpr<T>::m_column_key;
1983

1984
    using LeafType = typename ColumnTypeTraits<T>::cluster_leaf_type;
1985
    std::optional<LeafType> m_leaf;
1986
};
1987

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

1993
template <>
1994
class Columns<BinaryData> : public SimpleQuerySupport<BinaryData> {
1995
    using SimpleQuerySupport::SimpleQuerySupport;
1996

1997
    friend Query operator==(const Columns<BinaryData>& left, BinaryData right)
1998
    {
316✔
1999
        return create<Equal>(right, left);
316✔
2000
    }
316✔
2001

2002
    friend Query operator==(BinaryData left, const Columns<BinaryData>& right)
2003
    {
4✔
2004
        return create<Equal>(left, right);
4✔
2005
    }
4✔
2006

2007
    friend Query operator!=(const Columns<BinaryData>& left, BinaryData right)
2008
    {
238✔
2009
        return create<NotEqual>(right, left);
238✔
2010
    }
238✔
2011

2012
    friend Query operator!=(BinaryData left, const Columns<BinaryData>& right)
2013
    {
2✔
2014
        return create<NotEqual>(left, right);
2✔
2015
    }
2✔
2016

2017
    friend Query operator==(const Columns<BinaryData>& left, realm::null)
2018
    {
12✔
2019
        return create<Equal>(BinaryData(), left);
12✔
2020
    }
12✔
2021

2022
    friend Query operator==(realm::null, const Columns<BinaryData>& right)
2023
    {
×
2024
        return create<Equal>(BinaryData(), right);
×
2025
    }
×
2026

2027
    friend Query operator!=(const Columns<BinaryData>& left, realm::null)
2028
    {
×
2029
        return create<NotEqual>(BinaryData(), left);
×
2030
    }
×
2031

2032
    friend Query operator!=(realm::null, const Columns<BinaryData>& right)
2033
    {
×
2034
        return create<NotEqual>(BinaryData(), right);
×
2035
    }
×
2036
};
2037

2038
template <>
2039
class Columns<ObjectId> : public SimpleQuerySupport<ObjectId> {
2040
    using SimpleQuerySupport::SimpleQuerySupport;
2041
};
2042

2043
template <>
2044
class Columns<Decimal128> : public SimpleQuerySupport<Decimal128> {
2045
    using SimpleQuerySupport::SimpleQuerySupport;
2046
};
2047

2048
template <>
2049
class Columns<Mixed> : public SimpleQuerySupport<Mixed> {
2050
public:
2051
    using SimpleQuerySupport::evaluate; // don't hide the ObjKey overload
2052
    using SimpleQuerySupport::SimpleQuerySupport;
2053
    void set_base_table(ConstTableRef table) override
2054
    {
1,612✔
2055
        SimpleQuerySupport::set_base_table(table);
1,612✔
2056
        auto target_table = get_link_map().get_target_table();
1,612✔
2057
        m_ctrl.alloc = &target_table->get_alloc();
1,612✔
2058
        m_ctrl.group = table->get_parent_group();
1,612✔
2059
        m_ctrl.interner = target_table->get_string_interner(m_column_key);
1,612✔
2060
    }
1,612✔
2061

2062
    void evaluate(Subexpr::Index& index, ValueBase& destination) override
2063
    {
43,576✔
2064
        if (m_ctrl.path.empty()) {
43,576✔
2065
            destination.init(false, 1); // Size of 1 is expected by SimpleQuerySupport
41,704✔
2066
            SimpleQuerySupport::evaluate(index, destination);
41,704✔
2067
        }
41,704✔
2068
        else {
1,872✔
2069
            if (index.initialize()) {
1,872✔
2070
                Value<Mixed> dest;
1,560✔
2071
                dest.init(false, 1);
1,560✔
2072
                SimpleQuerySupport::evaluate(index, dest);
1,560✔
2073

×
2074
                m_ctrl.matches.clear();
1,560✔
2075
                if (auto sz = dest.size()) {
1,560✔
2076
                    for (size_t i = 0; i < sz; i++) {
3,072✔
2077
                        Collection::get_any(m_ctrl, dest.get(i), 0);
1,560✔
2078
                    }
1,560✔
2079
                }
1,512✔
2080
                if (!index.set_size(m_ctrl.matches.size())) {
1,560✔
2081
                    destination.init(true, 0);
284✔
2082
                    return;
284✔
2083
                }
284✔
2084
            }
1,560✔
2085
            // Copy values over
×
2086
            auto& matches = m_ctrl.matches[index.get_and_incr_sub_index()];
1,588✔
2087
            auto sz = matches.size();
1,588✔
2088
            destination.init(!m_ctrl.path_only_unary_keys || sz == 0, sz);
1,588!
2089
            destination.set(matches.begin(), matches.end());
1,588✔
2090
        }
1,588✔
2091
    }
43,576✔
2092
    std::string description(util::serializer::SerialisationState& state) const override
2093
    {
956✔
2094
        return ObjPropertyExpr::description(state) + util::to_string(m_ctrl.path);
956✔
2095
    }
956✔
2096
    Columns<Mixed>& path(const Path& path)
2097
    {
748✔
2098
        for (auto& elem : path) {
2,028✔
2099
            if (elem.is_all()) {
2,028✔
2100
                m_ctrl.path_only_unary_keys = false;
196✔
2101
            }
196✔
2102
            m_ctrl.path.emplace_back(elem);
2,028✔
2103
        }
2,028✔
2104
        return *this;
748✔
2105
    }
748✔
2106
    bool has_path() const noexcept override
2107
    {
900✔
2108
        return !m_ctrl.path.empty();
900✔
2109
    }
900✔
2110

2111
private:
2112
    Collection::QueryCtrlBlock m_ctrl;
2113
};
2114

2115
template <>
2116
class Columns<UUID> : public SimpleQuerySupport<UUID> {
2117
    using SimpleQuerySupport::SimpleQuerySupport;
2118
};
2119

2120
template <class T, class S, class I>
2121
inline std::enable_if_t<!realm::is_any_v<T, StringData, realm::null, const char*, std::string>, Query>
2122
string_compare(const Subexpr2<StringData>& left, T right, bool)
2123
{
×
2124
    return make_expression<Compare<Equal>>(right.clone(), left.clone());
×
2125
}
×
2126

2127
template <class T, class S, class I>
2128
inline std::enable_if_t<realm::is_any_v<T, StringData, realm::null, const char*, std::string>, Query>
2129
string_compare(const Subexpr2<StringData>& left, T right, bool case_sensitive)
2130
{
12,334✔
2131
    StringData sd(right);
12,334✔
2132
    if (case_sensitive)
12,334✔
2133
        return create<S>(sd, left);
7,990✔
2134
    else
4,344✔
2135
        return create<I>(sd, left);
4,344✔
2136
}
12,334✔
2137

2138
template <class S, class I>
2139
Query string_compare(const Subexpr2<StringData>& left, const Subexpr2<StringData>& right, bool case_sensitive)
2140
{
3,960✔
2141
    if (case_sensitive)
3,960✔
2142
        return make_expression<Compare<S>>(right.clone(), left.clone());
2,268✔
2143
    else
1,692✔
2144
        return make_expression<Compare<I>>(right.clone(), left.clone());
1,692✔
2145
}
3,960✔
2146

2147
template <>
2148
class Columns<StringData> : public SimpleQuerySupport<StringData> {
2149
public:
2150
    Columns(ColKey column, ConstTableRef table, const std::vector<ExtendedColumnKey>& links = {},
2151
            util::Optional<ExpressionComparisonType> type = util::none)
2152
        : SimpleQuerySupport(column, table, links, type)
9,650✔
2153
    {
19,308✔
2154
    }
19,308✔
2155

2156
    Columns(Columns const& other)
2157
        : SimpleQuerySupport(other)
6,254✔
2158
    {
12,508✔
2159
    }
12,508✔
2160

2161
    Columns(Columns&& other) noexcept
2162
        : SimpleQuerySupport(other)
2163
    {
×
2164
    }
×
2165

2166
    Query fulltext(StringData sd) const;
2167

2168
    using SimpleQuerySupport::size;
2169

2170
    // Columns<String> == Columns<String>
2171
    friend Query operator==(const Columns<StringData>& left, const Columns<StringData>& right)
2172
    {
192✔
2173
        return string_compare<Equal, EqualIns>(left, right, true);
192✔
2174
    }
192✔
2175

2176
    // Columns<String> != Columns<String>
2177
    friend Query operator!=(const Columns<StringData>& left, const Columns<StringData>& right)
2178
    {
188✔
2179
        return string_compare<NotEqual, NotEqualIns>(left, right, true);
188✔
2180
    }
188✔
2181

2182
    // String == Columns<String>
2183
    template <class T>
2184
    friend Query operator==(T left, const Columns<StringData>& right)
2185
    {
4✔
2186
        return operator==(right, left);
4✔
2187
    }
4✔
2188

2189
    // String != Columns<String>
2190
    template <class T>
2191
    friend Query operator!=(T left, const Columns<StringData>& right)
2192
    {
2✔
2193
        return operator!=(right, left);
2✔
2194
    }
2✔
2195

2196
    // Columns<String> == String
2197
    template <class T>
2198
    friend Query operator==(const Columns<StringData>& left, T right)
2199
    {
1,644✔
2200
        return string_compare<T, Equal, EqualIns>(left, right, true);
1,644✔
2201
    }
1,644✔
2202

2203
    // Columns<String> != String
2204
    template <class T>
2205
    friend Query operator!=(const Columns<StringData>& left, T right)
2206
    {
482✔
2207
        return string_compare<T, NotEqual, NotEqualIns>(left, right, true);
482✔
2208
    }
482✔
2209
};
2210

2211
template <class T, class S, class I>
2212
Query binary_compare(const Subexpr2<BinaryData>& left, T right, bool case_sensitive)
2213
{
×
2214
    BinaryData data(right);
×
2215
    if (case_sensitive)
×
2216
        return create<S>(data, left);
×
2217
    else
×
2218
        return create<I>(data, left);
×
2219
}
×
2220

2221
template <class S, class I>
2222
Query binary_compare(const Subexpr2<BinaryData>& left, const Subexpr2<BinaryData>& right, bool case_sensitive)
2223
{
×
2224
    if (case_sensitive)
×
2225
        return make_expression<Compare<S>>(right.clone(), left.clone());
×
2226
    else
×
2227
        return make_expression<Compare<I>>(right.clone(), left.clone());
×
2228
}
×
2229

2230
template <class T, class S, class I>
2231
Query mixed_compare(const Subexpr2<Mixed>& left, T right, bool case_sensitive)
2232
{
66✔
2233
    Mixed data(right);
66✔
2234
    if (case_sensitive)
66✔
2235
        return create<S>(data, left);
30✔
2236
    else
36✔
2237
        return create<I>(data, left);
36✔
2238
}
66✔
2239

2240
template <class S, class I>
2241
Query mixed_compare(const Subexpr2<Mixed>& left, const Subexpr2<Mixed>& right, bool case_sensitive)
2242
{
×
2243
    if (case_sensitive)
×
2244
        return make_expression<Compare<S>>(right.clone(), left.clone());
×
2245
    else
×
2246
        return make_expression<Compare<I>>(right.clone(), left.clone());
×
2247
}
×
2248

2249

2250
// This class is intended to perform queries on the *pointers* of links, contrary to performing queries on *payload*
2251
// in linked-to tables. Queries can be "find first link that points at row X" or "find first null-link". Currently
2252
// only "find first null link" and "find first non-null link" is supported. More will be added later. When we add
2253
// more, I propose to remove the <bool has_links> template argument from this class and instead template it by
2254
// a criteria-class (like the FindNullLinks class below in find_first()) in some generalized fashion.
2255
template <bool has_links>
2256
class UnaryLinkCompare : public Expression {
2257
public:
2258
    UnaryLinkCompare(const LinkMap& lm)
2259
        : m_link_map(lm)
63✔
2260
    {
126✔
2261
    }
126✔
2262

2263
    void set_base_table(ConstTableRef table) override
2264
    {
152✔
2265
        m_link_map.set_base_table(table);
152✔
2266
    }
152✔
2267

2268
    void set_cluster(const Cluster* cluster) override
2269
    {
208✔
2270
        m_link_map.set_cluster(cluster);
208✔
2271
    }
208✔
2272

2273
    void collect_dependencies(std::vector<TableKey>& tables) const override
2274
    {
28✔
2275
        m_link_map.collect_dependencies(tables);
28✔
2276
    }
28✔
2277

2278
    // Return main table of query (table on which table->where()... is invoked). Note that this is not the same as
2279
    // any linked-to payload tables
2280
    ConstTableRef get_base_table() const override
2281
    {
126✔
2282
        return m_link_map.get_base_table();
126✔
2283
    }
126✔
2284

2285
    size_t find_first(size_t start, size_t end) const override
2286
    {
1,574✔
2287
        for (; start < end; ++start) {
3,056✔
2288
            bool found_link = false;
2,926✔
2289
            m_link_map.map_links(start, [&](ObjKey) {
2,926✔
2290
                found_link = true;
1,502✔
2291
                return false; // we've found a key, so this can't be a null-link, so exit link harvesting
1,502✔
2292
            });
1,502✔
2293
            if (found_link == has_links)
2,926✔
2294
                return start;
1,444✔
2295
        }
2,926✔
2296

2297
        return not_found;
130✔
2298
    }
1,574✔
2299

2300
    std::string description(util::serializer::SerialisationState& state) const override
2301
    {
×
2302
        return state.describe_columns(m_link_map, ColKey()) + (has_links ? " != NULL" : " == NULL");
×
2303
    }
×
2304

2305
    std::unique_ptr<Expression> clone() const override
2306
    {
114✔
2307
        return std::unique_ptr<Expression>(new UnaryLinkCompare(*this));
114✔
2308
    }
114✔
2309

2310
private:
2311
    UnaryLinkCompare(const UnaryLinkCompare& other)
2312
        : Expression(other)
57✔
2313
        , m_link_map(other.m_link_map)
57✔
2314
    {
114✔
2315
    }
114✔
2316

2317
    mutable LinkMap m_link_map;
2318
};
2319

2320
class LinkCount : public Subexpr2<Int> {
2321
public:
2322
    LinkCount(const LinkMap& link_map)
2323
        : m_link_map(link_map)
426✔
2324
    {
852✔
2325
        if (m_link_map.get_nb_hops() > 1) {
852✔
2326
            m_column_key = m_link_map.pop_last();
44✔
2327
        }
44✔
2328
    }
852✔
2329
    LinkCount(LinkCount const& other)
2330
        : Subexpr2<Int>(other)
1,260✔
2331
        , m_link_map(other.m_link_map)
1,260✔
2332
        , m_column_key(other.m_column_key)
1,260✔
2333
    {
2,520✔
2334
    }
2,520✔
2335

2336
    std::unique_ptr<Subexpr> clone() const override
2337
    {
2,520✔
2338
        return make_subexpr<LinkCount>(*this);
2,520✔
2339
    }
2,520✔
2340

2341
    ConstTableRef get_base_table() const override
2342
    {
1,284✔
2343
        return m_link_map.get_base_table();
1,284✔
2344
    }
1,284✔
2345

2346
    void set_base_table(ConstTableRef table) override
2347
    {
1,464✔
2348
        m_link_map.set_base_table(table);
1,464✔
2349
    }
1,464✔
2350

2351
    void set_cluster(const Cluster* cluster) override
2352
    {
1,962✔
2353
        m_link_map.set_cluster(cluster);
1,962✔
2354
    }
1,962✔
2355

2356
    void collect_dependencies(std::vector<TableKey>& tables) const override
2357
    {
30✔
2358
        m_link_map.collect_dependencies(tables);
30✔
2359
    }
30✔
2360

2361
    void evaluate(Subexpr::Index& index, ValueBase& destination) override;
2362

2363
    std::string description(util::serializer::SerialisationState& state) const override;
2364

2365
private:
2366
    LinkMap m_link_map;
2367
    ColKey m_column_key;
2368
};
2369

2370
// Gives a count of all backlinks across all columns for the specified row.
2371
// The unused template parameter is a hack to avoid a circular dependency between table.hpp and query_expression.hpp.
2372
template <class>
2373
class BacklinkCount : public Subexpr2<Int> {
2374
public:
2375
    BacklinkCount(const LinkMap& link_map)
2376
        : m_link_map(link_map)
2377
    {
2378
    }
2379
    BacklinkCount(LinkMap&& link_map)
2380
        : m_link_map(std::move(link_map))
2381
    {
2382
    }
2383
    BacklinkCount(ConstTableRef table, std::vector<ExtendedColumnKey>&& links = {})
2384
        : m_link_map(table, std::move(links))
152✔
2385
    {
304✔
2386
    }
304✔
2387
    BacklinkCount(BacklinkCount const& other)
2388
        : Subexpr2<Int>(other)
284✔
2389
        , m_link_map(other.m_link_map)
284✔
2390
    {
568✔
2391
    }
568✔
2392

2393
    std::unique_ptr<Subexpr> clone() const override
2394
    {
568✔
2395
        return make_subexpr<BacklinkCount<Int>>(*this);
568✔
2396
    }
568✔
2397

2398
    ConstTableRef get_base_table() const override
2399
    {
264✔
2400
        return m_link_map.get_base_table();
264✔
2401
    }
264✔
2402

2403
    void set_base_table(ConstTableRef table) override
2404
    {
264✔
2405
        m_link_map.set_base_table(table);
264✔
2406
    }
264✔
2407

2408
    void set_cluster(const Cluster* cluster) override
2409
    {
264✔
2410
        if (m_link_map.has_links()) {
264✔
2411
            m_link_map.set_cluster(cluster);
88✔
2412
        }
88✔
2413
        else {
176✔
2414
            m_cluster = cluster;
176✔
2415
        }
176✔
2416
    }
264✔
2417

2418
    void collect_dependencies(std::vector<TableKey>& tables) const override
2419
    {
×
2420
        m_link_map.collect_dependencies(tables);
×
2421
    }
×
2422

2423
    void evaluate(Subexpr::Index& index, ValueBase& destination) override
2424
    {
872✔
2425
        size_t count;
872✔
2426
        if (m_link_map.has_links()) {
872✔
2427
            count = m_link_map.count_all_backlinks(index);
184✔
2428
        }
184✔
2429
        else {
688✔
2430
            const Obj obj = m_link_map.get_base_table()->get_object(m_cluster->get_real_key(index));
688✔
2431
            count = obj.get_backlink_count();
688✔
2432
        }
688✔
2433
        destination = Value<int64_t>(count);
872✔
2434
    }
872✔
2435

2436
    std::string description(util::serializer::SerialisationState& state) const override
2437
    {
132✔
2438
        std::string s;
132✔
2439
        if (m_link_map.links_exist()) {
132✔
2440
            s += state.describe_columns(m_link_map, ColKey()) + util::serializer::value_separator;
44✔
2441
        }
44✔
2442
        s += "@links.@count";
132✔
2443
        return s;
132✔
2444
    }
132✔
2445

2446
private:
2447
    const Cluster* m_cluster = nullptr;
2448
    LinkMap m_link_map;
2449
};
2450

2451
#if REALM_ENABLE_GEOSPATIAL
2452
class GeoWithinCompare : public Expression {
2453
public:
2454
    GeoWithinCompare(const LinkMap& lm, Geospatial&& bounds, util::Optional<ExpressionComparisonType> comp_type)
2455
        : m_link_map(lm)
196✔
2456
        , m_bounds(std::move(bounds))
196✔
2457
        , m_region(m_bounds)
196✔
2458
        , m_comp_type(comp_type)
196✔
2459
    {
392✔
2460
        Status status = m_region.get_conversion_status();
392✔
2461
        if (!status.is_ok()) {
392✔
2462
            throw InvalidArgument(status.code(),
32✔
2463
                                  util::format("Invalid region in GEOWITHIN query for parameter '%1': '%2'", m_bounds,
32✔
2464
                                               status.reason()));
32✔
2465
        }
32✔
2466
    }
392✔
2467

2468
    GeoWithinCompare(const GeoWithinCompare& other)
2469
        : m_link_map(other.m_link_map)
92✔
2470
        , m_bounds(other.m_bounds)
92✔
2471
        , m_region(m_bounds)
92✔
2472
        , m_comp_type(other.m_comp_type)
92✔
2473
    {
184✔
2474
    }
184✔
2475

2476
    void set_base_table(ConstTableRef table) override
2477
    {
360✔
2478
        m_link_map.set_base_table(table);
360✔
2479
        m_coords_col = m_link_map.get_target_table()->get_column_key(Geospatial::c_geo_point_coords_col_name);
360✔
2480
        m_type_col = m_link_map.get_target_table()->get_column_key(Geospatial::c_geo_point_type_col_name);
360✔
2481
        if (!m_coords_col || !m_type_col || !m_coords_col.is_list() ||
360✔
2482
            m_coords_col.get_type() != ColumnType(ColumnType::Type::Double) ||
360✔
2483
            m_type_col.get_type() != ColumnType(ColumnType::Type::String) || m_type_col.is_collection()) {
360✔
2484
            util::serializer::SerialisationState none;
4✔
2485
            throw std::runtime_error(util::format(
4✔
2486
                "Query '%1' links to data in the wrong format for a geoWithin query", this->description(none)));
4✔
2487
        }
4✔
2488
        if (!m_link_map.get_target_table()->is_embedded()) {
356✔
2489
            throw std::runtime_error(util::format(
4✔
2490
                "A GEOWITHIN query can only operate on a link to an embedded class but '%1' is at the top level",
4✔
2491
                m_link_map.get_target_table()->get_class_name()));
4✔
2492
        }
4✔
2493
    }
356✔
2494

2495
    void set_cluster(const Cluster* cluster) override
2496
    {
340✔
2497
        m_link_map.set_cluster(cluster);
340✔
2498
    }
340✔
2499

2500
    void collect_dependencies(std::vector<TableKey>& tables) const override
2501
    {
28✔
2502
        m_link_map.collect_dependencies(tables);
28✔
2503
    }
28✔
2504

2505
    // Return main table of query (table on which table->where()... is invoked). Note that this is not the same as
2506
    // any linked-to payload tables
2507
    ConstTableRef get_base_table() const override
2508
    {
360✔
2509
        return m_link_map.get_base_table();
360✔
2510
    }
360✔
2511

2512
    size_t find_first(size_t start, size_t end) const override
2513
    {
896✔
2514
        auto table = m_link_map.get_target_table();
896✔
2515

2516
        while (start < end) {
2,664✔
2517
            bool found = false;
2,392✔
2518
            switch (m_comp_type.value_or(ExpressionComparisonType::Any)) {
2,392✔
2519
                case ExpressionComparisonType::Any: {
2,120✔
2520
                    m_link_map.map_links(start, [&](ObjKey key) {
2,156✔
2521
                        found = m_region.contains(
2,156✔
2522
                            Geospatial::point_from_obj(table->get_object(key), m_type_col, m_coords_col));
2,156✔
2523
                        return !found; // keep searching if not found, stop searching on first match
2,156✔
2524
                    });
2,156✔
2525
                    if (found)
2,120✔
2526
                        return start;
520✔
2527
                    break;
1,600✔
2528
                }
2,120✔
2529
                case ExpressionComparisonType::All: {
1,600✔
2530
                    m_link_map.map_links(start, [&](ObjKey key) {
136✔
2531
                        found = m_region.contains(
120✔
2532
                            Geospatial::point_from_obj(table->get_object(key), m_type_col, m_coords_col));
120✔
2533
                        return found; // keep searching until one the first non-match
120✔
2534
                    });
120✔
2535
                    if (found) // all matched
136✔
2536
                        return start;
4✔
2537
                    break;
132✔
2538
                }
136✔
2539
                case ExpressionComparisonType::None: {
136✔
2540
                    m_link_map.map_links(start, [&](ObjKey key) {
312✔
2541
                        found = m_region.contains(
312✔
2542
                            Geospatial::point_from_obj(table->get_object(key), m_type_col, m_coords_col));
312✔
2543
                        return !found; // keep searching until the first match
312✔
2544
                    });
312✔
2545
                    if (!found) // none matched
136✔
2546
                        return start;
100✔
2547
                    break;
36✔
2548
                }
136✔
2549
            }
2,392✔
2550
            start++;
1,768✔
2551
        }
1,768✔
2552

2553
        return not_found;
272✔
2554
    }
896✔
2555

2556
    std::string description(util::serializer::SerialisationState& state) const override
2557
    {
112✔
2558
        return state.describe_expression_type(m_comp_type) + state.describe_columns(m_link_map, ColKey()) +
112✔
2559
               " GEOWITHIN " + util::serializer::print_value(m_bounds);
112✔
2560
    }
112✔
2561

2562
    std::unique_ptr<Expression> clone() const override
2563
    {
184✔
2564
        return std::unique_ptr<Expression>(new GeoWithinCompare(*this));
184✔
2565
    }
184✔
2566

2567
private:
2568
    LinkMap m_link_map;
2569
    Geospatial m_bounds;
2570
    GeoRegion m_region;
2571
    ColKey m_type_col;
2572
    ColKey m_coords_col;
2573
    util::Optional<ExpressionComparisonType> m_comp_type;
2574
};
2575
#endif
2576

2577
template <class T>
2578
class SizeOperator : public Subexpr2<Int> {
2579
public:
2580
    SizeOperator(std::unique_ptr<Subexpr> left)
2581
        : m_expr(std::move(left))
3,042✔
2582
    {
6,084✔
2583
    }
6,084✔
2584

2585
    SizeOperator(const SizeOperator& other)
2586
        : m_expr(other.m_expr->clone())
5,952✔
2587
    {
11,904✔
2588
    }
11,904✔
2589

2590
    // See comment in base class
2591
    void set_base_table(ConstTableRef table) override
2592
    {
6,384✔
2593
        m_expr->set_base_table(table);
6,384✔
2594
    }
6,384✔
2595

2596
    void set_cluster(const Cluster* cluster) override
2597
    {
6,780✔
2598
        m_expr->set_cluster(cluster);
6,780✔
2599
    }
6,780✔
2600

2601
    // Recursively fetch tables of columns in expression tree. Used when user first builds a stand-alone expression
2602
    // and binds it to a Query at a later time
2603
    ConstTableRef get_base_table() const override
2604
    {
6,084✔
2605
        return m_expr->get_base_table();
6,084✔
2606
    }
6,084✔
2607

2608
    // destination = operator(left)
2609
    void evaluate(Subexpr::Index& index, ValueBase& destination) override
2610
    {
74,016✔
2611
        Value<T> v;
74,016✔
2612
        m_expr->evaluate(index, v);
74,016✔
2613

2614
        size_t sz = v.size();
74,016✔
2615
        destination.init(v.m_from_list, sz);
74,016✔
2616

2617
        for (size_t i = 0; i < sz; i++) {
148,180✔
2618
            auto elem = v[i].template get<T>();
74,164✔
2619
            if constexpr (std::is_same_v<T, int64_t>) {
74,164✔
2620
                // This is the size of a list
2621
                destination.set(i, elem);
58,596✔
2622
            }
29,298✔
2623
            else if constexpr (std::is_same_v<T, Mixed>) {
18,028✔
2624
                if (elem.is_null()) {
17,550✔
2625
                    destination.set_null(i);
9,782✔
2626
                }
9,782✔
2627
                else if (elem.is_type(type_String)) {
10,706✔
2628
                    destination.set(i, int64_t(elem.get_string().size()));
9,958✔
2629
                }
9,958✔
2630
                else if (elem.is_type(type_List)) {
10,514✔
2631
                    DummyParent parent(m_expr->get_base_table().cast_away_const(), elem.get_ref(), ColKey());
9,874✔
2632
                    Lst<Mixed> list(parent, 0);
9,874✔
2633
                    destination.set(i, int64_t(list.size()));
9,874✔
2634
                }
9,874✔
2635
                else if (elem.is_type(type_Dictionary)) {
10,406✔
2636
                    DummyParent parent(m_expr->get_base_table().cast_away_const(), elem.get_ref(), ColKey());
9,806✔
2637
                    Dictionary dict(parent, 0);
9,806✔
2638
                    destination.set(i, int64_t(dict.size()));
9,806✔
2639
                }
9,806✔
2640
                else if (elem.is_type(type_Binary)) {
10,366✔
2641
                    destination.set(i, int64_t(elem.get_binary().size()));
9,774✔
2642
                }
9,774✔
2643
            }
10,244✔
2644
            else {
24,378✔
2645
                if (!elem) {
24,378✔
2646
                    destination.set_null(i);
9,886✔
2647
                }
9,886✔
2648
                else {
24,258✔
2649
                    destination.set(i, int64_t(elem.size()));
24,258✔
2650
                }
24,258✔
2651
            }
24,378✔
2652
        }
74,164✔
2653
    }
74,016✔
2654

2655
    std::string description(util::serializer::SerialisationState& state) const override
2656
    {
2,408✔
2657
        if (m_expr) {
2,408✔
2658
            return m_expr->description(state) + util::serializer::value_separator + "@size";
2,408✔
2659
        }
2,408✔
2660
        return "@size";
×
2661
    }
2,408✔
2662

2663
    std::unique_ptr<Subexpr> clone() const override
2664
    {
11,904✔
2665
        return std::unique_ptr<Subexpr>(new SizeOperator(*this));
11,904✔
2666
    }
11,904✔
2667

2668
private:
2669
    std::unique_ptr<Subexpr> m_expr;
2670
};
2671

2672
template <class T>
2673
class TypeOfValueOperator : public Subexpr2<TypeOfValue> {
2674
public:
2675
    TypeOfValueOperator(std::unique_ptr<Subexpr> left)
2676
        : m_expr(std::move(left))
396✔
2677
    {
792✔
2678
    }
792✔
2679

2680
    TypeOfValueOperator(const TypeOfValueOperator& other)
2681
        : m_expr(other.m_expr->clone())
784✔
2682
    {
1,568✔
2683
    }
1,568✔
2684

2685
    util::Optional<ExpressionComparisonType> get_comparison_type() const override
2686
    {
16,112✔
2687
        return m_expr->get_comparison_type();
16,112✔
2688
    }
16,112✔
2689

2690
    // See comment in base class
2691
    void set_base_table(ConstTableRef table) override
2692
    {
756✔
2693
        m_expr->set_base_table(table);
756✔
2694
    }
756✔
2695

2696
    void set_cluster(const Cluster* cluster) override
2697
    {
1,548✔
2698
        m_expr->set_cluster(cluster);
1,548✔
2699
    }
1,548✔
2700

2701
    // Recursively fetch tables of columns in expression tree. Used when user first builds a stand-alone expression
2702
    // and binds it to a Query at a later time
2703
    ConstTableRef get_base_table() const override
2704
    {
756✔
2705
        return m_expr->get_base_table();
756✔
2706
    }
756✔
2707

2708
    // destination = operator(left)
2709
    void evaluate(Subexpr::Index& index, ValueBase& destination) override
2710
    {
47,776✔
2711
        Value<T> v;
47,776✔
2712
        m_expr->evaluate(index, v);
47,776✔
2713

2714
        size_t sz = v.size();
47,776✔
2715
        destination.init(v.m_from_list, sz);
47,776✔
2716

2717
        for (size_t i = 0; i < sz; i++) {
98,128✔
2718
            auto elem = v[i].template get<T>();
50,352✔
2719
            destination.set(i, TypeOfValue(elem));
50,352✔
2720
        }
50,352✔
2721
    }
47,776✔
2722

2723
    std::string description(util::serializer::SerialisationState& state) const override
2724
    {
416✔
2725
        if (m_expr) {
416✔
2726
            return m_expr->description(state) + util::serializer::value_separator + "@type";
416✔
2727
        }
416✔
2728
        return "@type";
×
2729
    }
416✔
2730

2731
    std::unique_ptr<Subexpr> clone() const override
2732
    {
1,568✔
2733
        return std::unique_ptr<Subexpr>(new TypeOfValueOperator(*this));
1,568✔
2734
    }
1,568✔
2735

2736
private:
2737
    std::unique_ptr<Subexpr> m_expr;
2738
};
2739

2740
class KeyValue : public Subexpr2<Link> {
2741
public:
2742
    KeyValue(ObjKey key)
2743
        : m_key(key)
32✔
2744
    {
64✔
2745
    }
64✔
2746

2747
    void set_base_table(ConstTableRef) override {}
216✔
2748

2749
    ConstTableRef get_base_table() const override
2750
    {
64✔
2751
        return nullptr;
64✔
2752
    }
64✔
2753

2754
    void evaluate(Subexpr::Index&, ValueBase& destination) override
2755
    {
624✔
2756
        destination = Value<ObjKey>(m_key);
624✔
2757
    }
624✔
2758

2759
    std::string description(util::serializer::SerialisationState&) const override
2760
    {
4✔
2761
        return util::serializer::print_value(m_key);
4✔
2762
    }
4✔
2763

2764
    std::unique_ptr<Subexpr> clone() const override
2765
    {
256✔
2766
        return std::unique_ptr<Subexpr>(new KeyValue(*this));
256✔
2767
    }
256✔
2768

2769
private:
2770
    KeyValue(const KeyValue& source)
2771
        : m_key(source.m_key)
128✔
2772
    {
256✔
2773
    }
256✔
2774

2775
    ObjKey m_key;
2776
};
2777

2778
template <typename T>
2779
class SubColumns;
2780

2781
// This is for LinkList and BackLink too since they're declared as typedefs of Link.
2782
template <>
2783
class Columns<Link> : public Subexpr2<Link> {
2784
public:
2785
    Columns(const Columns& other)
2786
        : Subexpr2<Link>(other)
333✔
2787
        , m_link_map(other.m_link_map)
333✔
2788
        , m_comparison_type(other.m_comparison_type)
333✔
2789
        , m_is_list(other.m_is_list)
333✔
2790
    {
666✔
2791
    }
666✔
2792

2793
    Columns(ColKey column_key, ConstTableRef table, const std::vector<ExtendedColumnKey>& links = {},
2794
            util::Optional<ExpressionComparisonType> type = util::none)
2795
        : m_link_map(table, links)
3,028✔
2796
        , m_comparison_type(type)
3,028✔
2797
        , m_is_list(column_key.is_list())
3,028✔
2798
    {
6,056✔
2799
    }
6,056✔
2800

2801
    Query is_null()
2802
    {
76✔
2803
        if (m_link_map.get_nb_hops() > 1)
76✔
2804
            throw Exception(ErrorCodes::InvalidQuery, "Combining link() and is_null() is currently not supported");
2✔
2805
        // Todo, it may be useful to support the above, but we would need to figure out an intuitive behaviour
2806
        return make_expression<UnaryLinkCompare<false>>(m_link_map);
74✔
2807
    }
76✔
2808

2809
    Query is_not_null()
2810
    {
52✔
2811
        if (m_link_map.get_nb_hops() > 1)
52✔
2812
            throw Exception(ErrorCodes::InvalidQuery,
×
2813
                            "Combining link() and is_not_null() is currently not supported");
×
2814
        // Todo, it may be useful to support the above, but we would need to figure out an intuitive behaviour
2815
        return make_expression<UnaryLinkCompare<true>>(m_link_map);
52✔
2816
    }
52✔
2817

2818
#if REALM_ENABLE_GEOSPATIAL
2819
    Query geo_within(Geospatial bounds) const
2820
    {
392✔
2821
        return make_expression<GeoWithinCompare>(m_link_map, std::move(bounds), m_comparison_type);
392✔
2822
    }
392✔
2823
#endif
2824

2825
    LinkCount count() const
2826
    {
852✔
2827
        return LinkCount(m_link_map);
852✔
2828
    }
852✔
2829

2830
    template <class T>
2831
    BacklinkCount<T> backlink_count() const
2832
    {
2833
        return BacklinkCount<T>(m_link_map);
2834
    }
2835

2836
    template <typename C>
2837
    SubColumns<C> column(ColKey column_key) const
2838
    {
3,028✔
2839
        // no need to pass along m_comparison_type because the only operations supported from
2840
        // the subsequent SubColumns are aggregate operations such as sum, min, max, avg where
2841
        // having
2842
        REALM_ASSERT_DEBUG(!m_comparison_type);
3,028✔
2843
        return SubColumns<C>(Columns<C>(column_key, m_link_map.get_target_table()), m_link_map);
3,028✔
2844
    }
3,028✔
2845

2846
    const LinkMap& link_map() const
2847
    {
3,210✔
2848
        return m_link_map;
3,210✔
2849
    }
3,210✔
2850

2851
    DataType get_type() const override
2852
    {
1,518✔
2853
        return type_Link;
1,518✔
2854
    }
1,518✔
2855

2856
    ConstTableRef get_target_table() const override
2857
    {
102✔
2858
        return link_map().get_target_table();
102✔
2859
    }
102✔
2860

2861
    bool has_multiple_values() const override
2862
    {
648✔
2863
        return m_is_list || !m_link_map.only_unary_links();
648✔
2864
    }
648✔
2865

2866
    ConstTableRef get_base_table() const override
2867
    {
1,092✔
2868
        return m_link_map.get_base_table();
1,092✔
2869
    }
1,092✔
2870

2871
    void set_base_table(ConstTableRef table) override
2872
    {
504✔
2873
        m_link_map.set_base_table(table);
504✔
2874
    }
504✔
2875

2876
    void set_cluster(const Cluster* cluster) override
2877
    {
318✔
2878
        REALM_ASSERT(m_link_map.has_links());
318✔
2879
        m_link_map.set_cluster(cluster);
318✔
2880
    }
318✔
2881

2882
    void collect_dependencies(std::vector<TableKey>& tables) const override
2883
    {
276✔
2884
        m_link_map.collect_dependencies(tables);
276✔
2885
    }
276✔
2886

2887
    std::string description(util::serializer::SerialisationState& state) const override
2888
    {
132✔
2889
        return state.describe_expression_type(m_comparison_type) + state.describe_columns(m_link_map, ColKey());
132✔
2890
    }
132✔
2891

2892
    util::Optional<ExpressionComparisonType> get_comparison_type() const override
2893
    {
1,620✔
2894
        return m_comparison_type;
1,620✔
2895
    }
1,620✔
2896

2897
    std::unique_ptr<Subexpr> clone() const override
2898
    {
666✔
2899
        return std::unique_ptr<Subexpr>(new Columns<Link>(*this));
666✔
2900
    }
666✔
2901

2902
    void evaluate(Subexpr::Index& index, ValueBase& destination) override;
2903

2904
private:
2905
    LinkMap m_link_map;
2906
    util::Optional<ExpressionComparisonType> m_comparison_type;
2907
    bool m_is_list;
2908
    friend class Table;
2909
    friend class LinkChain;
2910
};
2911

2912
template <typename T>
2913
class ListColumns;
2914
template <typename T, typename Operation>
2915
class CollectionColumnAggregate;
2916
namespace aggregate_operations {
2917
template <typename T>
2918
class Minimum;
2919
template <typename T>
2920
class Maximum;
2921
template <typename T>
2922
class Sum;
2923
template <typename T>
2924
class Average;
2925
} // namespace aggregate_operations
2926

2927
class ColumnListBase {
2928
public:
2929
    ColumnListBase(ColKey column_key, ConstTableRef table, const std::vector<ExtendedColumnKey>& links,
2930
                   util::Optional<ExpressionComparisonType> type = util::none)
2931
        : m_column_key(column_key)
13,020✔
2932
        , m_link_map(table, links)
13,020✔
2933
        , m_comparison_type(type)
13,020✔
2934
    {
26,040✔
2935
    }
26,040✔
2936

2937
    ColumnListBase(const ColumnListBase& other)
2938
        : m_column_key(other.m_column_key)
37,647✔
2939
        , m_link_map(other.m_link_map)
37,647✔
2940
        , m_comparison_type(other.m_comparison_type)
37,647✔
2941
    {
75,294✔
2942
    }
75,294✔
2943

2944
    void set_cluster(const Cluster* cluster);
2945

2946
    void get_lists(size_t index, Value<int64_t>& destination);
2947

2948
    std::string description(util::serializer::SerialisationState& state) const
2949
    {
14,148✔
2950
        return state.describe_expression_type(m_comparison_type) + state.describe_columns(m_link_map, m_column_key);
14,148✔
2951
    }
14,148✔
2952

2953
    bool links_exist() const
2954
    {
39,534✔
2955
        return m_link_map.has_links();
39,534✔
2956
    }
39,534✔
2957

2958
    virtual SizeOperator<int64_t> size() = 0;
2959
    virtual std::unique_ptr<Subexpr> get_element_length() = 0;
2960
    virtual std::unique_ptr<Subexpr> max_of() = 0;
2961
    virtual std::unique_ptr<Subexpr> min_of() = 0;
2962
    virtual std::unique_ptr<Subexpr> sum_of() = 0;
2963
    virtual std::unique_ptr<Subexpr> avg_of() = 0;
2964

2965
    virtual bool index(const PathElement&) = 0;
2966

2967
    mutable ColKey m_column_key;
2968
    LinkMap m_link_map;
2969
    std::optional<ArrayInteger> m_leaf;
2970
    std::optional<ExpressionComparisonType> m_comparison_type;
2971
};
2972

2973
template <typename>
2974
class ColumnListSize;
2975

2976
template <typename T>
2977
class ColumnListElementLength;
2978

2979
template <typename T>
2980
class ColumnsCollection : public Subexpr2<T>, public ColumnListBase {
2981
public:
2982
    ColumnsCollection(ColKey column_key, ConstTableRef table, const std::vector<ExtendedColumnKey>& links = {},
2983
                      util::Optional<ExpressionComparisonType> type = util::none)
2984
        : ColumnListBase(column_key, table, links, type)
13,012✔
2985
        , m_is_nullable_storage(this->m_column_key.get_attrs().test(col_attr_Nullable))
13,012✔
2986
    {
26,024✔
2987
    }
26,024✔
2988

2989
    ColumnsCollection(const ColumnsCollection& other)
2990
        : Subexpr2<T>(other)
26,858✔
2991
        , ColumnListBase(other)
26,858✔
2992
        , m_is_nullable_storage(this->m_column_key.get_attrs().test(col_attr_Nullable))
26,858✔
2993
        , m_index(other.m_index)
26,858✔
2994
    {
53,716✔
2995
    }
53,716✔
2996

2997
    bool has_multiple_values() const override
2998
    {
204✔
2999
        return true;
204✔
3000
    }
204✔
3001

3002
    ConstTableRef get_base_table() const final
3003
    {
40,580✔
3004
        return m_link_map.get_base_table();
40,580✔
3005
    }
40,580✔
3006
    ConstTableRef get_target_table() const override
3007
    {
5,240✔
3008
        return m_link_map.get_target_table()->get_opposite_table(m_column_key);
5,240✔
3009
    }
5,240✔
3010

3011
    Allocator& get_alloc() const
3012
    {
1,385,152✔
3013
        return m_link_map.get_target_table()->get_alloc();
1,385,152✔
3014
    }
1,385,152✔
3015

3016
    void set_base_table(ConstTableRef table) override
3017
    {
25,568✔
3018
        m_link_map.set_base_table(table);
25,568✔
3019
    }
25,568✔
3020

3021
    void set_cluster(const Cluster* cluster) final
3022
    {
30,076✔
3023
        ColumnListBase::set_cluster(cluster);
30,076✔
3024
    }
30,076✔
3025

3026
    void collect_dependencies(std::vector<TableKey>& tables) const final
3027
    {
728✔
3028
        m_link_map.collect_dependencies(tables);
728✔
3029
    }
728✔
3030

3031
    void evaluate(Subexpr::Index& index, ValueBase& destination) override
3032
    {
143,636✔
3033
        if constexpr (realm::is_any_v<T, ObjectId, Int, Bool, UUID>) {
143,448✔
3034
            if (m_is_nullable_storage) {
43,236✔
3035
                evaluate<util::Optional<T>>(index, destination);
8,892✔
3036
                return;
8,892✔
3037
            }
8,892✔
3038
        }
43,236✔
3039
        evaluate<T>(index, destination);
34,720✔
3040
    }
89,178✔
3041

3042
    std::string description(util::serializer::SerialisationState& state) const override
3043
    {
9,624✔
3044
        std::string index_string;
9,624✔
3045
        if (m_index) {
9,624!
3046
            PathElement p(*m_index);
584✔
3047
            index_string = "[" + util::to_string(p) + "]";
584✔
3048
        }
584✔
3049
        return ColumnListBase::description(state) + index_string;
9,624✔
3050
    }
9,624✔
3051

3052
    util::Optional<ExpressionComparisonType> get_comparison_type() const final
3053
    {
71,598✔
3054
        return ColumnListBase::m_comparison_type;
71,598✔
3055
    }
71,598✔
3056

3057
    SizeOperator<int64_t> size() override;
3058

3059
    ColumnListElementLength<T> element_lengths() const
3060
    {
1,652✔
3061
        return {*this};
1,652✔
3062
    }
1,652✔
3063

3064
    TypeOfValueOperator<T> type_of_value()
3065
    {
128✔
3066
        return TypeOfValueOperator<T>(this->clone());
128✔
3067
    }
128✔
3068

3069
    CollectionColumnAggregate<T, aggregate_operations::Minimum<T>> min() const
3070
    {
1,420✔
3071
        return {*this};
1,420✔
3072
    }
1,420✔
3073

3074
    CollectionColumnAggregate<T, aggregate_operations::Maximum<T>> max() const
3075
    {
1,460✔
3076
        return {*this};
1,460✔
3077
    }
1,460✔
3078

3079
    CollectionColumnAggregate<T, aggregate_operations::Sum<T>> sum() const
3080
    {
1,444✔
3081
        return {*this};
1,444✔
3082
    }
1,444✔
3083

3084
    CollectionColumnAggregate<T, aggregate_operations::Average<T>> average() const
3085
    {
1,256✔
3086
        return {*this};
1,256✔
3087
    }
1,256✔
3088

3089
    std::unique_ptr<Subexpr> max_of() override
3090
    {
796✔
3091
        if constexpr (realm::is_any_v<T, Int, Float, Double, Decimal128, Mixed>) {
796✔
3092
            return max().clone();
480✔
3093
        }
106✔
3094
        else {
130✔
3095
            return {};
130✔
3096
        }
130✔
3097
    }
796✔
3098
    std::unique_ptr<Subexpr> min_of() override
3099
    {
756✔
3100
        if constexpr (realm::is_any_v<T, Int, Float, Double, Decimal128, Mixed>) {
756✔
3101
            return min().clone();
452✔
3102
        }
98✔
3103
        else {
122✔
3104
            return {};
122✔
3105
        }
122✔
3106
    }
756✔
3107
    std::unique_ptr<Subexpr> sum_of() override
3108
    {
984✔
3109
        if constexpr (realm::is_any_v<T, Int, Float, Double, Decimal128, Mixed>) {
984✔
3110
            return sum().clone();
584✔
3111
        }
116✔
3112
        else {
140✔
3113
            return {};
140✔
3114
        }
140✔
3115
    }
984✔
3116
    std::unique_ptr<Subexpr> avg_of() override
3117
    {
824✔
3118
        if constexpr (realm::is_any_v<T, Int, Float, Double, Decimal128, Mixed>) {
824✔
3119
            return average().clone();
488✔
3120
        }
100✔
3121
        else {
124✔
3122
            return {};
124✔
3123
        }
124✔
3124
    }
824✔
3125

3126
    std::unique_ptr<Subexpr> get_element_length() override
3127
    {
1,716✔
3128
        if constexpr (realm::is_any_v<T, StringData, BinaryData, Mixed>) {
1,716✔
3129
            return element_lengths().clone();
864✔
3130
        }
38✔
3131
        else {
70✔
3132
            return {};
70✔
3133
        }
70✔
3134
    }
1,716✔
3135

3136
    std::unique_ptr<Subexpr> clone() const override
3137
    {
×
3138
        return std::unique_ptr<Subexpr>(new ColumnsCollection(*this));
×
3139
    }
×
3140

3141
    bool index(const PathElement& ndx) override
3142
    {
3,030✔
3143
        if (ndx.is_ndx()) {
3,030✔
3144
            m_index = ndx.get_ndx();
1,222✔
3145
            return true;
1,222✔
3146
        }
1,222✔
3147
        else if (ndx.is_all()) {
1,808!
3148
            return true;
204✔
3149
        }
204✔
3150
        return false;
1,604✔
3151
    }
3,030✔
3152
    const bool m_is_nullable_storage;
3153
    std::optional<size_t> m_index;
3154

3155
protected:
3156
    Value<int64_t> m_list_refs;
3157

3158
    template <typename StorageType>
3159
    void evaluate(Subexpr::Index& index, ValueBase& destination)
3160
    {
154,382✔
3161
        const bool is_from_list = true;
154,382✔
3162
        destination.init(is_from_list, 0);
154,382✔
3163

3164
        if (index.initialize()) {
154,382!
3165
            get_lists(index, m_list_refs);
153,884✔
3166
            if (!index.set_size(m_list_refs.size()))
153,884✔
3167
                return;
120✔
3168
        }
153,884✔
3169

3170
        std::vector<StorageType> values;
154,262✔
3171
        ref_type list_ref = to_ref(m_list_refs.get(index.get_and_incr_sub_index()).get_int());
154,262✔
3172
        if (list_ref) {
154,262!
3173
            BPlusTree<StorageType> list(get_alloc());
125,670✔
3174
            list.init_from_ref(list_ref);
125,670✔
3175
            if (size_t s = list.size()) {
125,670!
3176
                if (m_index) {
125,650!
3177
                    destination.init(is_from_list, 1);
4,758✔
3178
                    if (*m_index < s) {
4,758!
3179
                        destination.set(0, list.get(*m_index));
2,046✔
3180
                    }
2,046✔
3181
                    else if (*m_index == size_t(-1)) {
2,712!
3182
                        destination.set(0, list.get(s - 1));
1,560✔
3183
                    }
1,560✔
3184
                }
4,758✔
3185
                else {
120,892✔
3186
                    destination.init(is_from_list, s);
120,892✔
3187
                    for (size_t j = 0; j < s; j++) {
396,106!
3188
                        destination.set(j, list.get(j));
275,214✔
3189
                    }
275,214✔
3190
                }
120,892✔
3191
            }
125,650✔
3192
        }
125,670✔
3193
    }
154,262✔
3194
};
3195

3196
template <typename T>
3197
class Columns<Lst<T>> : public ColumnsCollection<T> {
3198
public:
3199
    using ColumnsCollection<T>::ColumnsCollection;
3200
    std::unique_ptr<Subexpr> clone() const override
3201
    {
8,216✔
3202
        return make_subexpr<Columns<Lst<T>>>(*this);
8,216✔
3203
    }
8,216✔
3204
};
3205

3206
template <>
3207
class Columns<Lst<String>> : public ColumnsCollection<String> {
3208
public:
3209
    using ColumnsCollection<String>::ColumnsCollection;
3210
    using ColumnListBase::m_column_key;
3211
    using ColumnListBase::m_link_map;
3212

3213
    std::unique_ptr<Subexpr> clone() const override
3214
    {
4,004✔
3215
        return make_subexpr<Columns<Lst<String>>>(*this);
4,004✔
3216
    }
4,004✔
3217

3218
    bool has_search_index() const final
3219
    {
1,872✔
3220
        auto target_table = m_link_map.get_target_table();
1,872✔
3221
        return target_table->search_index_type(m_column_key) == IndexType::General;
1,872✔
3222
    }
1,872✔
3223

3224
    std::vector<ObjKey> find_all(Mixed value) const final
3225
    {
572✔
3226
        std::vector<ObjKey> ret;
572✔
3227
        std::vector<ObjKey> result;
572✔
3228

3229
        StringIndex* index = m_link_map.get_target_table()->get_string_index(m_column_key);
572✔
3230
        REALM_ASSERT(index);
572✔
3231
        index->find_all(result, value);
572✔
3232

3233
        for (ObjKey k : result) {
604✔
3234
            auto ndxs = m_link_map.get_origin_objkeys(k);
604✔
3235
            ret.insert(ret.end(), ndxs.begin(), ndxs.end());
604✔
3236
        }
604✔
3237

3238
        return ret;
572✔
3239
    }
572✔
3240
};
3241

3242
template <typename T>
3243
class Columns<Set<T>> : public ColumnsCollection<T> {
3244
public:
3245
    using ColumnsCollection<T>::ColumnsCollection;
3246
    std::unique_ptr<Subexpr> clone() const override
3247
    {
1,792✔
3248
        return make_subexpr<Columns<Set<T>>>(*this);
1,792✔
3249
    }
1,792✔
3250
    bool index(const PathElement&) override
3251
    {
144✔
3252
        return false;
144✔
3253
    }
144✔
3254
};
3255

3256

3257
template <>
3258
class Columns<LnkLst> : public Columns<Lst<ObjKey>> {
3259
public:
3260
    using Columns<Lst<ObjKey>>::Columns;
3261

3262
    std::unique_ptr<Subexpr> clone() const override
3263
    {
×
3264
        return make_subexpr<Columns<LnkLst>>(*this);
×
3265
    }
×
3266
};
3267

3268
template <>
3269
class Columns<LnkSet> : public Columns<Set<ObjKey>> {
3270
public:
3271
    using Columns<Set<ObjKey>>::Columns;
3272

3273
    std::unique_ptr<Subexpr> clone() const override
3274
    {
×
3275
        return make_subexpr<Columns<LnkSet>>(*this);
×
3276
    }
×
3277
};
3278

3279
template <>
3280
class Columns<Lst<Mixed>> : public ColumnsCollection<Mixed> {
3281
public:
3282
    using ColumnsCollection<Mixed>::ColumnsCollection;
3283
    std::unique_ptr<Subexpr> clone() const override
3284
    {
468✔
3285
        return make_subexpr<Columns<Lst<Mixed>>>(*this);
468✔
3286
    }
468✔
3287
    friend class Table;
3288
    friend class LinkChain;
3289
    bool indexes(const Path& path)
3290
    {
52✔
3291
        REALM_ASSERT(!path.empty());
52✔
3292
        if (ColumnsCollection<Mixed>::index(path[0])) {
52✔
3293
            for (auto& elem : path) {
84✔
3294
                m_ctrl.path.emplace_back(elem);
84✔
3295
            }
84✔
3296
            return true;
44✔
3297
        }
44✔
3298
        return false;
8✔
3299
    }
52✔
3300
    Columns<Lst<Mixed>>& path(const Path& path)
3301
    {
2✔
3302
        if (!indexes(path)) {
2✔
3303
            throw InvalidArgument("Illegal path");
×
3304
        }
×
3305
        return *this;
2✔
3306
    }
2✔
3307
    std::string description(util::serializer::SerialisationState& state) const override
3308
    {
112✔
3309
        return ColumnListBase::description(state) + util::to_string(m_ctrl.path);
112✔
3310
    }
112✔
3311
    void set_base_table(ConstTableRef table) override
3312
    {
272✔
3313
        ColumnsCollection::set_base_table(table);
272✔
3314
        auto target_table = m_link_map.get_target_table();
272✔
3315
        m_ctrl.alloc = &target_table->get_alloc();
272✔
3316
        m_ctrl.group = table->get_parent_group();
272✔
3317
        m_ctrl.interner = target_table->get_string_interner(m_column_key);
272✔
3318
    }
272✔
3319

3320
    void evaluate(Subexpr::Index& index, ValueBase& destination) override
3321
    {
7,164✔
3322
        if (m_ctrl.path.size() > 1) {
7,164✔
3323
            if (index.initialize()) {
80✔
3324
                m_sub_index = index;
76✔
3325
                m_ctrl.matches.clear();
76✔
3326

3327
                do {
80✔
3328
                    Value<Mixed> destination;
80✔
3329
                    ColumnsCollection<Mixed>::evaluate<Mixed>(m_sub_index, destination);
80✔
3330

3331
                    if (auto sz = destination.size()) {
80✔
3332
                        for (size_t i = 0; i < sz; i++) {
160✔
3333
                            Collection::get_any(m_ctrl, destination.get(i), 1);
80✔
3334
                        }
80✔
3335
                    }
80✔
3336
                } while (m_sub_index.more());
80✔
3337

3338
                if (!index.set_size(m_ctrl.matches.size())) {
76✔
3339
                    destination.init(true, 0);
×
3340
                    return;
×
3341
                }
×
3342
            }
76✔
3343
            // Copy values over
3344
            auto& matches = m_ctrl.matches[index.get_and_incr_sub_index()];
80✔
3345
            auto sz = matches.size();
80✔
3346
            destination.init(!m_ctrl.path_only_unary_keys || sz == 0, sz);
80!
3347
            destination.set(matches.begin(), matches.end());
80✔
3348
        }
80✔
3349
        else {
7,084✔
3350
            // Base class will handle path[0] and return result in destination
3351
            ColumnsCollection<Mixed>::evaluate<Mixed>(index, destination);
7,084✔
3352
        }
7,084✔
3353
    }
7,164✔
3354

3355
private:
3356
    Collection::QueryCtrlBlock m_ctrl;
3357
    Subexpr::Index m_sub_index = 0;
3358
};
3359

3360
// Returns the keys
3361
class ColumnDictionaryKeys;
3362

3363
// Returns the values
3364
template <>
3365
class Columns<Dictionary> : public ColumnsCollection<Mixed> {
3366
public:
3367
    Columns(ColKey column, ConstTableRef table, const std::vector<ExtendedColumnKey>& links = {},
3368
            util::Optional<ExpressionComparisonType> type = util::none)
3369
        : ColumnsCollection<Mixed>(column, table, links, type)
730✔
3370
    {
1,460✔
3371
        m_key_type = m_link_map.get_target_table()->get_dictionary_key_type(m_column_key);
1,460✔
3372
        m_ctrl.path.push_back(PathElement::AllTag());
1,460✔
3373
    }
1,460✔
3374

3375
    Columns(const Path& path, ConstTableRef table, const std::vector<ExtendedColumnKey>& links = {},
3376
            util::Optional<ExpressionComparisonType> type = util::none)
3377
        : ColumnsCollection<Mixed>(path[0].get_col_key(), table, links, type)
3378
    {
×
3379
        size_t path_size = path.size();
×
3380
        REALM_ASSERT(path_size > 0);
×
3381
        if (path_size == 1) {
×
3382
            m_key_type = m_link_map.get_target_table()->get_dictionary_key_type(m_column_key);
×
3383
        }
×
3384
        init_path(&path[1], &path[1] + path_size - 1);
×
3385
    }
×
3386

3387
    // Change the node to handle a specific key value only
3388
    Columns<Dictionary>& key(StringData key)
3389
    {
208✔
3390
        PathElement p(key);
208✔
3391
        init_path(&p, &p + 1);
208✔
3392
        return *this;
208✔
3393
    }
208✔
3394

3395
    // Change the node to handle a specific key value only
3396
    Columns<Dictionary>& path(const Path& path)
3397
    {
196✔
3398
        auto sz = path.size();
196✔
3399
        const PathElement* first = &path[0];
196✔
3400
        init_path(first, first + sz);
196✔
3401
        return *this;
196✔
3402
    }
196✔
3403

3404
    DataType get_key_type() const
3405
    {
90✔
3406
        return m_key_type;
90✔
3407
    }
90✔
3408

3409
    ColumnDictionaryKeys keys();
3410

3411
    void set_base_table(ConstTableRef table) override
3412
    {
984✔
3413
        ColumnsCollection::set_base_table(table);
984✔
3414
        auto target_table = m_link_map.get_target_table();
984✔
3415
        m_ctrl.alloc = &target_table->get_alloc();
984✔
3416
        m_ctrl.group = table->get_parent_group();
984✔
3417
        m_ctrl.interner = target_table->get_string_interner(m_column_key);
984✔
3418
    }
984✔
3419
    SizeOperator<int64_t> size() override;
3420
    std::unique_ptr<Subexpr> get_element_length() override
3421
    {
×
3422
        // Not supported for Dictionary
3423
        return {};
×
3424
    }
×
3425

3426
    void evaluate(Subexpr::Index& index, ValueBase& destination) override;
3427

3428
    std::string description(util::serializer::SerialisationState& state) const override
3429
    {
138✔
3430
        return ColumnListBase::description(state) + util::to_string(m_ctrl.path);
138✔
3431
    }
138✔
3432

3433
    std::unique_ptr<Subexpr> clone() const override
3434
    {
1,128✔
3435
        return make_subexpr<Columns<Dictionary>>(*this);
1,128✔
3436
    }
1,128✔
3437

3438
    bool index(const PathElement&) override
3439
    {
×
3440
        return false;
×
3441
    }
×
3442

3443
    Columns(Columns const& other)
3444
        : ColumnsCollection<Mixed>(other)
582✔
3445
        , m_key_type(other.m_key_type)
582✔
3446
        , m_ctrl(other.m_ctrl)
582✔
3447
    {
1,164✔
3448
    }
1,164✔
3449

3450
protected:
3451
    DataType m_key_type = type_String;
3452
    Collection::QueryCtrlBlock m_ctrl;
3453

3454
    void init_path(const PathElement* begin, const PathElement* end);
3455
};
3456

3457
// Returns the keys
3458
class ColumnDictionaryKeys : public Subexpr2<StringData> {
3459
public:
3460
    ColumnDictionaryKeys(const Columns<Dictionary>& dict)
3461
        : m_key_type(dict.get_key_type())
45✔
3462
        , m_column_key(dict.m_column_key)
45✔
3463
        , m_link_map(dict.m_link_map)
45✔
3464
        , m_comparison_type(dict.get_comparison_type())
45✔
3465
    {
90✔
3466
        REALM_ASSERT(m_key_type == type_String);
90✔
3467
    }
90✔
3468

3469
    ConstTableRef get_base_table() const final
3470
    {
6,696✔
3471
        return m_link_map.get_base_table();
6,696✔
3472
    }
6,696✔
3473

3474
    void set_base_table(ConstTableRef table) final
3475
    {
90✔
3476
        m_link_map.set_base_table(table);
90✔
3477
    }
90✔
3478

3479
    void collect_dependencies(std::vector<TableKey>& tables) const final
3480
    {
12✔
3481
        m_link_map.collect_dependencies(tables);
12✔
3482
    }
12✔
3483

3484
    util::Optional<ExpressionComparisonType> get_comparison_type() const final
3485
    {
2,082✔
3486
        return m_comparison_type;
2,082✔
3487
    }
2,082✔
3488

3489
    void set_cluster(const Cluster* cluster) override;
3490
    void evaluate(Subexpr::Index& index, ValueBase& destination) override;
3491

3492
    std::string description(util::serializer::SerialisationState& state) const override
3493
    {
30✔
3494
        return state.describe_expression_type(m_comparison_type) + state.describe_columns(m_link_map, m_column_key) +
30✔
3495
               ".@keys";
30✔
3496
    }
30✔
3497

3498
    std::unique_ptr<Subexpr> clone() const override
3499
    {
102✔
3500
        return std::unique_ptr<Subexpr>(new ColumnDictionaryKeys(*this));
102✔
3501
    }
102✔
3502

3503
    ColumnDictionaryKeys(const ColumnDictionaryKeys& other)
3504
        : m_key_type(other.m_key_type)
51✔
3505
        , m_column_key(other.m_column_key)
51✔
3506
        , m_link_map(other.m_link_map)
51✔
3507
        , m_comparison_type(other.m_comparison_type)
51✔
3508
    {
102✔
3509
    }
102✔
3510

3511
private:
3512
    DataType m_key_type;
3513
    ColKey m_column_key;
3514
    LinkMap m_link_map;
3515
    std::optional<ExpressionComparisonType> m_comparison_type;
3516
    std::optional<ArrayInteger> m_leaf;
3517
    std::vector<ObjKey> m_links;
3518
};
3519

3520
template <typename T>
3521
class ColumnListSize : public ColumnsCollection<T> {
3522
public:
3523
    ColumnListSize(const ColumnsCollection<T>& other)
3524
        : ColumnsCollection<T>(other)
1,786✔
3525
    {
3,572✔
3526
    }
3,572✔
3527

3528
    void evaluate(Subexpr::Index& index, ValueBase& destination) override
3529
    {
40,498✔
3530
        if constexpr (realm::is_any_v<T, ObjectId, Int, Bool, UUID>) {
39,319✔
3531
            if (this->m_is_nullable_storage) {
21,743✔
3532
                evaluate<util::Optional<T>>(index, destination);
4,227✔
3533
                return;
4,227✔
3534
            }
4,227✔
3535
        }
21,743✔
3536
        evaluate<T>(index, destination);
19,874✔
3537
    }
30,186✔
3538

3539
    std::unique_ptr<Subexpr> clone() const override
3540
    {
7,014✔
3541
        return std::unique_ptr<Subexpr>(new ColumnListSize<T>(*this));
7,014✔
3542
    }
7,014✔
3543

3544
private:
3545
    template <typename StorageType>
3546
    void evaluate(Subexpr::Index& index, ValueBase& destination)
3547
    {
40,498✔
3548
        Allocator& alloc = ColumnsCollection<T>::get_alloc();
40,498✔
3549
        Value<int64_t> list_refs;
40,498✔
3550
        this->get_lists(index, list_refs);
40,498✔
3551
        destination.init(list_refs.m_from_list, list_refs.size());
40,498✔
3552
        for (size_t i = 0; i < list_refs.size(); i++) {
81,100✔
3553
            ref_type list_ref = to_ref(list_refs[i].get_int());
40,602✔
3554
            if (list_ref) {
40,602✔
3555
                BPlusTree<StorageType> list(alloc);
24,348✔
3556
                list.init_from_ref(list_ref);
24,348✔
3557
                size_t s = list.size();
24,348✔
3558
                destination.set(i, int64_t(s));
24,348✔
3559
            }
24,348✔
3560
            else {
16,254✔
3561
                destination.set(i, 0);
16,254✔
3562
            }
16,254✔
3563
        }
40,602✔
3564
    }
40,498✔
3565
};
3566

3567

3568
template <typename T>
3569
class ColumnListElementLength : public Subexpr2<Int> {
3570
public:
3571
    ColumnListElementLength(const ColumnsCollection<T>& source)
3572
        : m_list(source)
826✔
3573
    {
1,652✔
3574
    }
1,652✔
3575
    bool has_multiple_values() const override
3576
    {
120✔
3577
        return true;
120✔
3578
    }
120✔
3579
    void evaluate(Subexpr::Index& index, ValueBase& destination) override
3580
    {
10,028✔
3581
        Allocator& alloc = m_list.get_alloc();
10,028✔
3582
        Value<int64_t> list_refs;
10,028✔
3583
        m_list.get_lists(index, list_refs);
10,028✔
3584
        std::vector<Int> sizes;
10,028✔
3585
        for (size_t i = 0; i < list_refs.size(); i++) {
20,056✔
3586
            ref_type list_ref = to_ref(list_refs[i].get_int());
10,028✔
3587
            if (list_ref) {
10,028✔
3588
                BPlusTree<T> list(alloc);
8,376✔
3589
                list.init_from_ref(list_ref);
8,376✔
3590
                const size_t list_size = list.size();
8,376✔
3591
                sizes.reserve(sizes.size() + list_size);
8,376✔
3592
                for (size_t j = 0; j < list_size; j++) {
19,560✔
3593
                    if constexpr (std::is_same_v<T, Mixed>) {
11,184✔
3594
                        Mixed v = list.get(j);
5,712✔
3595
                        if (!v.is_null()) {
5,712✔
3596
                            if (v.get_type() == type_String) {
600✔
3597
                                sizes.push_back(v.get_string().size());
120✔
3598
                            }
120✔
3599
                            else if (v.get_type() == type_Binary) {
480✔
3600
                                sizes.push_back(v.get_binary().size());
24✔
3601
                            }
24✔
3602
                        }
600✔
3603
                    }
360✔
3604
                    else {
10,584✔
3605
                        sizes.push_back(list.get(j).size());
10,584✔
3606
                    }
10,584✔
3607
                }
11,184✔
3608
            }
8,376✔
3609
        }
10,028✔
3610
        constexpr bool is_from_list = true;
10,028✔
3611
        destination.init(is_from_list, sizes.size());
10,028✔
3612
        destination.set(sizes.begin(), sizes.end());
10,028✔
3613
    }
10,028✔
3614
    ConstTableRef get_base_table() const override
3615
    {
1,652✔
3616
        return m_list.get_base_table();
1,652✔
3617
    }
1,652✔
3618

3619
    void set_base_table(ConstTableRef table) override
3620
    {
1,652✔
3621
        m_list.set_base_table(table);
1,652✔
3622
    }
1,652✔
3623

3624
    void set_cluster(const Cluster* cluster) override
3625
    {
1,652✔
3626
        m_list.set_cluster(cluster);
1,652✔
3627
    }
1,652✔
3628

3629
    void collect_dependencies(std::vector<TableKey>& tables) const override
3630
    {
×
3631
        m_list.collect_dependencies(tables);
×
3632
    }
×
3633

3634
    std::unique_ptr<Subexpr> clone() const override
3635
    {
3,304✔
3636
        return std::unique_ptr<Subexpr>(new ColumnListElementLength<T>(*this));
3,304✔
3637
    }
3,304✔
3638

3639
    std::string description(util::serializer::SerialisationState& state) const override
3640
    {
826✔
3641
        return m_list.description(state) + util::serializer::value_separator + "length";
826✔
3642
    }
826✔
3643

3644
    util::Optional<ExpressionComparisonType> get_comparison_type() const override
3645
    {
5,300✔
3646
        return m_list.get_comparison_type();
5,300✔
3647
    }
5,300✔
3648

3649
private:
3650
    ColumnsCollection<T> m_list;
3651
};
3652

3653

3654
template <typename T>
3655
SizeOperator<int64_t> ColumnsCollection<T>::size()
3656
{
3,572✔
3657
    std::unique_ptr<Subexpr> ptr(new ColumnListSize<T>(*this));
3,572✔
3658
    return SizeOperator<int64_t>(std::move(ptr));
3,572✔
3659
}
3,572✔
3660

3661
template <typename T, typename Operation>
3662
class CollectionColumnAggregate : public Subexpr2<decltype(Operation().result())> {
3663
public:
3664
    CollectionColumnAggregate(ColumnsCollection<T> column)
3665
        : m_columns_collection(std::move(column))
2,790✔
3666
    {
5,580✔
3667
        if (m_columns_collection.m_column_key.is_dictionary()) {
5,580✔
3668
            m_dictionary_key_type = m_columns_collection.m_link_map.get_target_table()->get_dictionary_key_type(
1,104✔
3669
                m_columns_collection.m_column_key);
1,104✔
3670
        }
1,104✔
3671
    }
5,580✔
3672

3673
    CollectionColumnAggregate(const CollectionColumnAggregate& other)
3674
        : m_columns_collection(other.m_columns_collection)
5,434✔
3675
        , m_dictionary_key_type(other.m_dictionary_key_type)
5,434✔
3676
    {
10,868✔
3677
    }
10,868✔
3678

3679
    DataType get_type() const override
3680
    {
5,952✔
3681
        return DataType(m_columns_collection.m_column_key.get_type());
5,952✔
3682
    }
5,952✔
3683

3684
    std::unique_ptr<Subexpr> clone() const override
3685
    {
10,868✔
3686
        return make_subexpr<CollectionColumnAggregate>(*this);
10,868✔
3687
    }
10,868✔
3688

3689
    ConstTableRef get_base_table() const override
3690
    {
5,580✔
3691
        return m_columns_collection.get_base_table();
5,580✔
3692
    }
5,580✔
3693

3694
    void set_base_table(ConstTableRef table) override
3695
    {
6,060✔
3696
        m_columns_collection.set_base_table(table);
6,060✔
3697
    }
6,060✔
3698

3699
    void set_cluster(const Cluster* cluster) override
3700
    {
10,396✔
3701
        m_columns_collection.set_cluster(cluster);
10,396✔
3702
    }
10,396✔
3703

3704
    void collect_dependencies(std::vector<TableKey>& tables) const override
3705
    {
488✔
3706
        m_columns_collection.collect_dependencies(tables);
488✔
3707
    }
488✔
3708

3709
    void evaluate(Subexpr::Index& index, ValueBase& destination) override
3710
    {
1,132,576✔
3711
        if (m_dictionary_key_type) {
1,132,576✔
3712
            if (m_columns_collection.links_exist()) {
17,508!
3713
                std::vector<ObjKey> links = m_columns_collection.m_link_map.get_links(index);
1,212✔
3714
                auto sz = links.size();
1,212✔
3715

3716
                destination.init_for_links(m_columns_collection.m_link_map.only_unary_links(), sz);
1,212✔
3717
                if (sz == 0 && m_columns_collection.m_link_map.only_unary_links()) {
1,212!
3718
                    set_value_for_empty_dictionary(destination, 0);
324✔
3719
                }
324✔
3720
                for (size_t t = 0; t < sz; t++) {
3,180!
3721
                    const Obj obj = m_columns_collection.m_link_map.get_target_table()->get_object(links[t]);
1,968✔
3722
                    auto dict = obj.get_dictionary(m_columns_collection.m_column_key);
1,968✔
3723
                    if (dict.size() > 0) {
1,968!
3724
                        destination.set(t, do_dictionary_agg(dict));
1,644✔
3725
                    }
1,644✔
3726
                    else {
324✔
3727
                        set_value_for_empty_dictionary(destination, t);
324✔
3728
                    }
324✔
3729
                }
1,968✔
3730
            }
1,212✔
3731
            else {
16,296✔
3732
                if (auto ref = m_columns_collection.m_leaf->get(index)) {
16,296!
3733
                    Allocator& alloc = m_columns_collection.get_base_table()->get_alloc();
15,732✔
3734
                    Dictionary dict(alloc, m_columns_collection.m_column_key, to_ref(ref));
15,732✔
3735
                    destination.set(0, do_dictionary_agg(dict));
15,732✔
3736
                }
15,732✔
3737
                else {
564✔
3738
                    set_value_for_empty_dictionary(destination, 0);
564✔
3739
                }
564✔
3740
            }
16,296✔
3741
        }
17,508✔
3742
        else {
1,115,068✔
3743
            Allocator& alloc = m_columns_collection.get_alloc();
1,115,068✔
3744
            Value<int64_t> list_refs;
1,115,068✔
3745
            m_columns_collection.get_lists(index, list_refs);
1,115,068✔
3746
            size_t sz = list_refs.size();
1,115,068✔
3747
            REALM_ASSERT_DEBUG(sz > 0 || list_refs.m_from_list);
1,115,068!
3748
            // The result is an aggregate value for each table
3749
            destination.init_for_links(!list_refs.m_from_list, sz);
1,115,068✔
3750
            for (size_t i = 0; i < list_refs.size(); i++) {
2,230,480✔
3751
                auto list_ref = to_ref(list_refs[i].get_int());
1,115,412✔
3752
                Operation op;
1,115,412✔
3753
                if (list_ref) {
1,115,412✔
3754
                    if constexpr (realm::is_any_v<T, ObjectId, Int, Bool, UUID>) {
1,085,728✔
3755
                        if (m_columns_collection.m_is_nullable_storage) {
543,186✔
3756
                            accumulate<util::Optional<T>>(op, alloc, list_ref);
189,476✔
3757
                        }
189,476✔
3758
                        else {
32,880✔
3759
                            accumulate<T>(op, alloc, list_ref);
32,880✔
3760
                        }
32,880✔
3761
                    }
111,178✔
3762
                    else {
865,304✔
3763
                        accumulate<T>(op, alloc, list_ref);
865,304✔
3764
                    }
865,304✔
3765
                }
1,086,372✔
3766
                if (op.is_null()) {
1,115,412✔
3767
                    destination.set_null(i);
23,036✔
3768
                }
23,036✔
3769
                else {
1,092,376✔
3770
                    destination.set(i, op.result());
1,092,376✔
3771
                }
1,092,376✔
3772
            }
1,115,412✔
3773
        }
1,115,068✔
3774
    }
1,132,576✔
3775

3776
    std::string description(util::serializer::SerialisationState& state) const override
3777
    {
1,584✔
3778
        return m_columns_collection.description(state) + util::serializer::value_separator + Operation::description();
1,584✔
3779
    }
1,584✔
3780

3781
private:
3782
    template <typename StorageType>
3783
    void accumulate(Operation& op, Allocator& alloc, ref_type list_ref)
3784
    {
1,086,372✔
3785
        BPlusTree<StorageType> list(alloc);
1,086,372✔
3786
        list.init_from_ref(list_ref);
1,086,372✔
3787
        size_t s = list.size();
1,086,372✔
3788
        for (unsigned j = 0; j < s; j++) {
6,433,936✔
3789
            auto v = list.get(j);
5,347,564✔
3790
            if (!value_is_null(v)) {
5,347,564✔
3791
                if constexpr (std::is_same_v<StorageType, util::Optional<T>>) {
4,473,812✔
3792
                    op.accumulate(*v);
2,238,046✔
3793
                }
1,845,758✔
3794
                else {
3,691,516✔
3795
                    op.accumulate(v);
3,691,516✔
3796
                }
3,691,516✔
3797
            }
4,476,092✔
3798
        }
5,347,564✔
3799
    }
1,086,372✔
3800

3801
    Mixed do_dictionary_agg(const Dictionary& dict)
3802
    {
17,376✔
3803
        if constexpr (std::is_same_v<Operation, aggregate_operations::Maximum<Mixed>>) {
17,376✔
3804
            return *dict.do_max();
8,688✔
3805
        }
6,030✔
3806
        else if constexpr (std::is_same_v<Operation, aggregate_operations::Minimum<Mixed>>) {
8,028✔
3807
            return *dict.do_min();
6,030✔
3808
        }
4,032✔
3809
        else if constexpr (std::is_same_v<Operation, aggregate_operations::Average<Mixed>>) {
6,030✔
3810
            return *dict.do_avg();
4,032✔
3811
        }
2,034✔
3812
        else if constexpr (std::is_same_v<Operation, aggregate_operations::Sum<Mixed>>) {
4,068✔
3813
            return *dict.do_sum();
4,068✔
3814
        }
4,068✔
3815
        REALM_UNREACHABLE();
3816
    }
8,688✔
3817

3818
    inline void set_value_for_empty_dictionary(ValueBase& destination, size_t ndx)
3819
    {
1,212✔
3820
        if constexpr (std::is_same_v<Operation, aggregate_operations::Sum<Mixed>>) {
1,212✔
3821
            destination.set(ndx, 0); // the sum of nothing is zero
606✔
3822
        }
414✔
3823
        else {
828✔
3824
            destination.set_null(ndx);
828✔
3825
        }
828✔
3826
    }
1,212✔
3827

3828
    ColumnsCollection<T> m_columns_collection;
3829
    util::Optional<DataType> m_dictionary_key_type;
3830
};
3831

3832
template <class Operator>
3833
Query compare(const Subexpr2<Link>& left, const Obj& obj)
3834
{
280✔
3835
    static_assert(std::is_same_v<Operator, Equal> || std::is_same_v<Operator, NotEqual>,
280✔
3836
                  "Links can only be compared for equality.");
280✔
3837
    const Columns<Link>* column = dynamic_cast<const Columns<Link>*>(&left);
280✔
3838
    if (column) {
280✔
3839
        const LinkMap& link_map = column->link_map();
280✔
3840
        REALM_ASSERT(link_map.get_target_table()->get_key() == obj.get_table()->get_key());
280✔
3841
#ifdef REALM_OLDQUERY_FALLBACK
280✔
3842
        if (link_map.get_nb_hops() == 1) {
280✔
3843
            // We can fall back to Query::links_to for != and == operations on links
3844
            if (link_map.m_link_types[0] == col_type_Link) {
274✔
3845
                ConstTableRef t = column->get_base_table();
272✔
3846
                Query query(t);
272✔
3847

3848
                if (std::is_same_v<Operator, Equal>) {
272✔
3849
                    return query.equal(link_map.m_link_column_keys[0], obj.get_link());
166✔
3850
                }
166✔
3851
                else {
106✔
3852
                    return query.not_equal(link_map.m_link_column_keys[0], obj.get_link());
106✔
3853
                }
106✔
3854
            }
272✔
3855
        }
274✔
3856
#endif
280✔
3857
    }
280✔
3858
    return make_expression<Compare<Operator>>(left.clone(), make_subexpr<KeyValue>(obj.get_key()));
8✔
3859
}
280✔
3860
template <class Operator>
3861
Query compare(const Subexpr2<Link>& left, null)
3862
{
42✔
3863
    static_assert(std::is_same_v<Operator, Equal> || std::is_same_v<Operator, NotEqual>,
42✔
3864
                  "Links can only be compared for equality.");
42✔
3865
    return make_expression<Compare<Operator>>(left.clone(), make_subexpr<KeyValue>(ObjKey{}));
42✔
3866
}
42✔
3867

3868

3869
template <class T>
3870
class Columns : public ObjPropertyExpr<T> {
3871
    constexpr static bool requires_null_column = realm::is_any_v<T, int64_t, bool>;
3872

3873
public:
3874
    using LeafType = typename ColumnTypeTraits<T>::cluster_leaf_type;
3875
    using NullableLeafType =
3876
        std::conditional_t<requires_null_column, typename ColumnTypeTraits<util::Optional<T>>::cluster_leaf_type,
3877
                           LeafType>;
3878
    using ObjPropertyExpr<T>::links_exist;
3879
    using ObjPropertyBase::is_nullable;
3880

3881
    Columns(ColKey column, ConstTableRef table, const std::vector<ExtendedColumnKey>& links = {},
3882
            util::Optional<ExpressionComparisonType> type = util::none)
3883
        : ObjPropertyExpr<T>(column, table, links, type)
22,972✔
3884
    {
45,964✔
3885
    }
45,964✔
3886

3887
    Columns(const Columns& other)
3888
        : ObjPropertyExpr<T>(other)
17,264✔
3889
    {
34,528✔
3890
    }
34,528✔
3891

3892
    void set_cluster(const Cluster* cluster) override
3893
    {
64,892✔
3894
        if (links_exist()) {
64,892✔
3895
            m_link_map.set_cluster(cluster);
2,432✔
3896
            m_leaf = mpark::monostate();
2,432✔
3897
        }
2,432✔
3898
        else if (requires_null_column && is_nullable()) {
62,460✔
3899
            auto& leaf = m_leaf.template emplace<NullableLeafType>(this->get_base_table()->get_alloc());
10,620✔
3900
            cluster->init_leaf(m_column_key, &leaf);
10,620✔
3901
        }
10,620✔
3902
        else {
51,840✔
3903
            auto& leaf = m_leaf.template emplace<LeafType>(this->get_base_table()->get_alloc());
51,840✔
3904
            cluster->init_leaf(m_column_key, &leaf);
51,840✔
3905
        }
51,840✔
3906
    }
64,892✔
3907

3908
    template <class LeafType2 = LeafType>
3909
    void evaluate_internal(size_t index, ValueBase& destination)
3910
    {
2,911,612✔
3911
        using U = typename LeafType2::value_type;
2,911,612✔
3912

3913
        if (links_exist()) {
2,911,612✔
3914
            REALM_ASSERT(mpark::holds_alternative<mpark::monostate>(m_leaf));
69,712✔
3915
            if (m_link_map.only_unary_links()) {
69,712✔
3916
                destination.init(false, 1);
20,216✔
3917
                destination.set_null(0);
20,216✔
3918
                if (auto link_translation_key = m_link_map.get_unary_link_or_not_found(index)) {
20,216✔
3919
                    const Obj obj = m_link_map.get_target_table()->get_object(link_translation_key);
19,752✔
3920
                    if (!obj.is_null(m_column_key))
19,752✔
3921
                        destination.set(0, obj.get<U>(m_column_key));
18,334✔
3922
                }
19,752✔
3923
            }
20,216✔
3924
            else {
49,496✔
3925
                // LinkList with more than 0 values. Create Value with payload for all fields
3926
                std::vector<ObjKey> links = m_link_map.get_links(index);
49,496✔
3927
                destination.init_for_links(m_link_map.only_unary_links(), links.size());
49,496✔
3928

3929
                for (size_t t = 0; t < links.size(); t++) {
148,596✔
3930
                    const Obj obj = m_link_map.get_target_table()->get_object(links[t]);
99,100✔
3931
                    if (obj.is_null(m_column_key))
99,100✔
3932
                        destination.set_null(t);
4✔
3933
                    else
99,096✔
3934
                        destination.set(t, obj.get<U>(m_column_key));
99,096✔
3935
                }
99,100✔
3936
            }
49,496✔
3937
        }
69,712✔
3938
        else {
2,841,900✔
3939
            auto leaf = mpark::get_if<LeafType2>(&m_leaf);
2,841,900✔
3940
            REALM_ASSERT(leaf);
2,841,900✔
3941
            // Not a Link column
3942
            size_t colsize = leaf->size();
2,841,900✔
3943

3944
            size_t rows = std::min(colsize - index, ValueBase::chunk_size);
2,841,900✔
3945

3946
            // Now load `ValueBase::chunk_size` rows from from the leaf into m_storage.
3947
            if constexpr (std::is_same_v<U, int64_t>) {
2,841,900✔
3948
                // If it's an integer leaf, then it contains the method get_chunk() which copies
3949
                // these values in a super fast way. If you want to modify 'chunk_size' then update Array::get_chunk()
3950
                REALM_ASSERT_3(ValueBase::chunk_size, ==, 8);
2,033,176✔
3951

3952
                int64_t res[ValueBase::chunk_size];
2,033,176✔
3953
                static_cast<const Array*>(leaf)->get_chunk(index, res);
2,033,176✔
3954
                destination.init(false, rows);
2,033,176✔
3955
                destination.set(res, res + rows);
2,033,176✔
3956
                return;
2,033,176✔
3957
            }
2,033,176✔
3958

UNCOV
3959
            destination.init(false, rows);
×
3960
            for (size_t t = 0; t < rows; t++) {
7,992,952✔
3961
                if (leaf->is_null(index + t)) {
6,206,216✔
3962
                    destination.set_null(t);
780,954✔
3963
                }
780,954✔
3964
                else {
5,425,262✔
3965
                    destination.set(t, leaf->get(index + t));
5,425,262✔
3966
                }
5,425,262✔
3967
            }
6,206,216✔
3968
        }
1,309,734✔
3969
    }
2,911,612✔
3970

3971
    std::string description(util::serializer::SerialisationState& state) const override
3972
    {
2,412✔
3973
        return state.describe_expression_type(this->m_comparison_type) +
2,412✔
3974
               state.describe_columns(m_link_map, m_column_key);
2,412✔
3975
    }
2,412✔
3976

3977
    // Load values from Column into destination
3978
    void evaluate(Subexpr::Index& index, ValueBase& destination) override
3979
    {
2,911,586✔
3980
        if (is_nullable()) {
2,911,586✔
3981
            evaluate_internal<NullableLeafType>(index, destination);
775,980✔
3982
        }
775,980✔
3983
        else {
2,135,606✔
3984
            evaluate_internal<LeafType>(index, destination);
2,135,606✔
3985
        }
2,135,606✔
3986
    }
2,911,586✔
3987

3988
    void evaluate(ObjKey key, ValueBase& destination)
3989
    {
38,040✔
3990
        destination.init(false, 1);
38,040✔
3991
        auto table = m_link_map.get_target_table();
38,040✔
3992
        auto obj = table.unchecked_ptr()->get_object(key);
38,040✔
3993
        if (requires_null_column && is_nullable()) {
38,040✔
3994
            destination.set(0, obj.template get<util::Optional<T>>(m_column_key));
1,224✔
3995
        }
1,224✔
3996
        else {
36,816✔
3997
            destination.set(0, obj.template get<T>(m_column_key));
36,816✔
3998
        }
36,816✔
3999
    }
38,040✔
4000

4001
private:
4002
    using ObjPropertyExpr<T>::m_link_map;
4003
    using ObjPropertyExpr<T>::m_column_key;
4004
    using LeafStorage =
4005
        std::conditional_t<requires_null_column, mpark::variant<mpark::monostate, LeafType, NullableLeafType>,
4006
                           mpark::variant<mpark::monostate, LeafType>>;
4007
    LeafStorage m_leaf;
4008
};
4009

4010
template <typename T, typename Operation>
4011
class SubColumnAggregate;
4012

4013
// Defines a uniform interface for aggregation methods.
4014
class SubColumnBase {
4015
public:
4016
    virtual std::unique_ptr<Subexpr> max_of() = 0;
4017
    virtual std::unique_ptr<Subexpr> min_of() = 0;
4018
    virtual std::unique_ptr<Subexpr> sum_of() = 0;
4019
    virtual std::unique_ptr<Subexpr> avg_of() = 0;
4020
};
4021

4022
template <typename T>
4023
class SubColumns : public Subexpr, public SubColumnBase {
4024
public:
4025
    SubColumns(Columns<T>&& column, const LinkMap& link_map)
4026
        : m_column(std::move(column))
1,514✔
4027
        , m_link_map(link_map)
1,514✔
4028
    {
3,028✔
4029
    }
3,028✔
4030

4031
    DataType get_type() const final
4032
    {
×
4033
        return ColumnTypeTraits<T>::id;
×
4034
    }
×
4035

4036
    std::unique_ptr<Subexpr> clone() const override
4037
    {
752✔
4038
        return make_subexpr<SubColumns<T>>(*this);
752✔
4039
    }
752✔
4040

4041
    ConstTableRef get_base_table() const override
4042
    {
×
4043
        return m_link_map.get_base_table();
×
4044
    }
×
4045

4046
    void set_base_table(ConstTableRef table) override
4047
    {
×
4048
        m_link_map.set_base_table(table);
×
4049
        m_column.set_base_table(m_link_map.get_target_table());
×
4050
    }
×
4051

4052
    void collect_dependencies(std::vector<TableKey>& tables) const override
4053
    {
×
4054
        m_link_map.collect_dependencies(tables);
×
4055
    }
×
4056

4057
    void evaluate(Subexpr::Index&, ValueBase&) override
4058
    {
×
4059
        // SubColumns can only be used in an expression in conjunction with its aggregate methods.
4060
        REALM_ASSERT(false);
×
4061
    }
×
4062

4063
    std::string description(util::serializer::SerialisationState&) const override
4064
    {
×
4065
        return ""; // by itself there are no conditions, see SubColumnAggregate
×
4066
    }
×
4067

4068
    SubColumnAggregate<T, aggregate_operations::Minimum<T>> min() const
4069
    {
760✔
4070
        return {m_column, m_link_map};
760✔
4071
    }
760✔
4072

4073
    SubColumnAggregate<T, aggregate_operations::Maximum<T>> max() const
4074
    {
784✔
4075
        return {m_column, m_link_map};
784✔
4076
    }
784✔
4077

4078
    SubColumnAggregate<T, aggregate_operations::Sum<T>> sum() const
4079
    {
712✔
4080
        return {m_column, m_link_map};
712✔
4081
    }
712✔
4082

4083
    SubColumnAggregate<T, aggregate_operations::Average<T>> average() const
4084
    {
764✔
4085
        return {m_column, m_link_map};
764✔
4086
    }
764✔
4087

4088
    std::unique_ptr<Subexpr> max_of() override
4089
    {
216✔
4090
        if constexpr (realm::is_any_v<T, Int, Float, Double, Decimal128, Timestamp, Mixed>) {
216✔
4091
            return max().clone();
216✔
4092
        }
108✔
4093
        else {
108✔
4094
            return {};
108✔
4095
        }
108✔
4096
    }
216✔
4097
    std::unique_ptr<Subexpr> min_of() override
4098
    {
208✔
4099
        if constexpr (realm::is_any_v<T, Int, Float, Double, Decimal128, Timestamp, Mixed>) {
208✔
4100
            return min().clone();
208✔
4101
        }
104✔
4102
        else {
104✔
4103
            return {};
104✔
4104
        }
104✔
4105
    }
208✔
4106
    std::unique_ptr<Subexpr> sum_of() override
4107
    {
148✔
4108
        if constexpr (realm::is_any_v<T, Int, Float, Double, Decimal128, Mixed>) {
148✔
4109
            return sum().clone();
74✔
4110
        }
2✔
4111
        else {
4✔
4112
            return {};
4✔
4113
        }
4✔
4114
    }
148✔
4115
    std::unique_ptr<Subexpr> avg_of() override
4116
    {
180✔
4117
        if constexpr (realm::is_any_v<T, Int, Float, Double, Decimal128, Mixed>) {
180✔
4118
            return average().clone();
90✔
4119
        }
2✔
4120
        else {
4✔
4121
            return {};
4✔
4122
        }
4✔
4123
    }
180✔
4124

4125
private:
4126
    Columns<T> m_column;
4127
    LinkMap m_link_map;
4128
};
4129

4130
template <typename T, typename Operation>
4131
class SubColumnAggregate : public Subexpr2<decltype(Operation().result())> {
4132
public:
4133
    SubColumnAggregate(const Columns<T>& column, const LinkMap& link_map)
4134
        : m_column(column)
1,510✔
4135
        , m_link_map(link_map)
1,510✔
4136
    {
3,020✔
4137
    }
3,020✔
4138
    SubColumnAggregate(SubColumnAggregate const& other)
4139
        : m_column(other.m_column)
2,932✔
4140
        , m_link_map(other.m_link_map)
2,932✔
4141
    {
5,864✔
4142
    }
5,864✔
4143

4144
    std::unique_ptr<Subexpr> clone() const override
4145
    {
5,864✔
4146
        return make_subexpr<SubColumnAggregate>(*this);
5,864✔
4147
    }
5,864✔
4148

4149
    ConstTableRef get_base_table() const override
4150
    {
3,020✔
4151
        return m_link_map.get_base_table();
3,020✔
4152
    }
3,020✔
4153

4154
    void set_base_table(ConstTableRef table) override
4155
    {
3,500✔
4156
        m_link_map.set_base_table(table);
3,500✔
4157
        m_column.set_base_table(m_link_map.get_target_table());
3,500✔
4158
    }
3,500✔
4159

4160
    void set_cluster(const Cluster* cluster) override
4161
    {
4,412✔
4162
        m_link_map.set_cluster(cluster);
4,412✔
4163
    }
4,412✔
4164

4165
    void collect_dependencies(std::vector<TableKey>& tables) const override
4166
    {
436✔
4167
        m_link_map.collect_dependencies(tables);
436✔
4168
    }
436✔
4169

4170
    void evaluate(Subexpr::Index& index, ValueBase& destination) override
4171
    {
59,268✔
4172
        std::vector<ObjKey> keys = m_link_map.get_links(index);
59,268✔
4173
        std::sort(keys.begin(), keys.end());
59,268✔
4174

4175
        Operation op;
59,268✔
4176
        for (auto key : keys) {
59,268✔
4177
            Value<T> value;
41,448✔
4178
            m_column.evaluate(key, value);
41,448✔
4179
            size_t value_index = 0;
41,448✔
4180
            if (!value[value_index].is_null()) {
41,448✔
4181
                op.accumulate(value[value_index].template get<T>());
41,052✔
4182
            }
41,052✔
4183
        }
41,448✔
4184
        if (op.is_null()) {
59,268✔
4185
            destination.set_null(0);
20,972✔
4186
        }
20,972✔
4187
        else {
38,296✔
4188
            destination.set(0, op.result());
38,296✔
4189
        }
38,296✔
4190
    }
59,268✔
4191

4192
    std::string description(util::serializer::SerialisationState& state) const override
4193
    {
372✔
4194
        util::serializer::SerialisationState empty_state(state.group);
372✔
4195
        return state.describe_columns(m_link_map, ColKey()) + util::serializer::value_separator +
372✔
4196
               Operation::description() + util::serializer::value_separator + m_column.description(empty_state);
372✔
4197
    }
372✔
4198

4199
private:
4200
    Columns<T> m_column;
4201
    LinkMap m_link_map;
4202
};
4203

4204
class SubQueryCount : public Subexpr2<Int> {
4205
public:
4206
    SubQueryCount(const Query& q, const LinkMap& link_map)
4207
        : m_query(q)
206✔
4208
        , m_link_map(link_map)
206✔
4209
    {
412✔
4210
        REALM_ASSERT(q.produces_results_in_table_order());
412✔
4211
        REALM_ASSERT(m_query.get_table() == m_link_map.get_target_table());
412✔
4212
    }
412✔
4213

4214
    ConstTableRef get_base_table() const override
4215
    {
412✔
4216
        return m_link_map.get_base_table();
412✔
4217
    }
412✔
4218

4219
    void set_base_table(ConstTableRef table) override
4220
    {
440✔
4221
        m_link_map.set_base_table(table);
440✔
4222
        m_query.set_table(m_link_map.get_target_table().cast_away_const());
440✔
4223
    }
440✔
4224

4225
    void set_cluster(const Cluster* cluster) override
4226
    {
588✔
4227
        m_link_map.set_cluster(cluster);
588✔
4228
    }
588✔
4229

4230
    void collect_dependencies(std::vector<TableKey>& tables) const override
4231
    {
32✔
4232
        m_link_map.collect_dependencies(tables);
32✔
4233
    }
32✔
4234

4235
    void evaluate(Subexpr::Index& index, ValueBase& destination) override
4236
    {
3,928✔
4237
        std::vector<ObjKey> links = m_link_map.get_links(index);
3,928✔
4238
        // std::sort(links.begin(), links.end());
4239
        if (!m_initialized) {
3,928✔
4240
            m_query.init();
452✔
4241
            m_initialized = true;
452✔
4242
        }
452✔
4243

4244
        size_t count = std::accumulate(links.begin(), links.end(), size_t(0), [this](size_t running_count, ObjKey k) {
6,988✔
4245
            const Obj obj = m_link_map.get_target_table()->get_object(k);
6,988✔
4246
            return running_count + m_query.eval_object(obj);
6,988✔
4247
        });
6,988✔
4248

4249
        destination = Value<int64_t>(count);
3,928✔
4250
    }
3,928✔
4251

4252
    std::string description(util::serializer::SerialisationState& state) const override
4253
    {
144✔
4254
        REALM_ASSERT(m_link_map.get_base_table() != nullptr);
144✔
4255
        std::string target = state.describe_columns(m_link_map, ColKey());
144✔
4256
        std::string var_name = state.get_variable_name(m_link_map.get_base_table());
144✔
4257
        state.subquery_prefix_list.push_back(var_name);
144✔
4258
        std::string desc = "SUBQUERY(" + target + ", " + var_name + ", " + m_query.get_description(state) + ")" +
144✔
4259
                           util::serializer::value_separator + "@count";
144✔
4260
        state.subquery_prefix_list.pop_back();
144✔
4261
        return desc;
144✔
4262
    }
144✔
4263

4264
    std::unique_ptr<Subexpr> clone() const override
4265
    {
560✔
4266
        return make_subexpr<SubQueryCount>(*this);
560✔
4267
    }
560✔
4268

4269
    SubQueryCount(const SubQueryCount& other)
4270
        : m_query(other.m_query)
280✔
4271
        , m_link_map(other.m_link_map)
280✔
4272
        , m_initialized(false)
280✔
4273
    {
560✔
4274
    }
560✔
4275

4276
private:
4277
    Query m_query;
4278
    LinkMap m_link_map;
4279
    bool m_initialized = false;
4280
};
4281

4282
// The unused template parameter is a hack to avoid a circular dependency between table.hpp and query_expression.hpp.
4283
template <class>
4284
class SubQuery {
4285
public:
4286
    SubQuery(Columns<Link> link_column, Query query)
4287
        : m_query(std::move(query))
33✔
4288
        , m_link_map(link_column.link_map())
33✔
4289
    {
66✔
4290
        REALM_ASSERT(m_link_map.get_target_table() == m_query.get_table());
66✔
4291
    }
66✔
4292

4293
    SubQueryCount count() const
4294
    {
66✔
4295
        return SubQueryCount(m_query, m_link_map);
66✔
4296
    }
66✔
4297

4298
private:
4299
    Query m_query;
4300
    LinkMap m_link_map;
4301
};
4302

4303
template <class oper>
4304
class Operator : public Subexpr2<Mixed> {
4305
public:
4306
    Operator(std::unique_ptr<Subexpr> left, std::unique_ptr<Subexpr> right)
4307
        : m_left(std::move(left))
640✔
4308
        , m_right(std::move(right))
640✔
4309
    {
1,280✔
4310
        m_left_is_const = m_left->has_single_value();
1,280✔
4311
        m_right_is_const = m_right->has_single_value();
1,280✔
4312
        if (m_left_is_const) {
1,280✔
4313
            m_const_value = m_left->get_mixed();
152✔
4314
        }
152✔
4315
        else if (m_right_is_const) {
1,128✔
4316
            m_const_value = m_right->get_mixed();
508✔
4317
        }
508✔
4318
    }
1,280✔
4319

4320
    Operator(const Operator& other)
4321
        : m_left(other.m_left->clone())
982✔
4322
        , m_right(other.m_right->clone())
982✔
4323
        , m_left_is_const(other.m_left_is_const)
982✔
4324
        , m_right_is_const(other.m_right_is_const)
982✔
4325
        , m_const_value(other.m_const_value)
982✔
4326
    {
1,964✔
4327
    }
1,964✔
4328

4329
    Operator& operator=(const Operator& other)
4330
    {
4331
        if (this != &other) {
4332
            m_left = other.m_left->clone();
4333
            m_right = other.m_right->clone();
4334
        }
4335
        return *this;
4336
    }
4337

4338
    Operator(Operator&&) noexcept = delete;
4339
    Operator& operator=(Operator&&) noexcept = delete;
4340

4341
    DataType get_type() const override
4342
    {
4,568✔
4343
        return m_left->get_type();
4,568✔
4344
    }
4,568✔
4345

4346
    // See comment in base class
4347
    void set_base_table(ConstTableRef table) override
4348
    {
1,280✔
4349
        m_left->set_base_table(table);
1,280✔
4350
        m_right->set_base_table(table);
1,280✔
4351
    }
1,280✔
4352

4353
    void set_cluster(const Cluster* cluster) override
4354
    {
22,360✔
4355
        m_left->set_cluster(cluster);
22,360✔
4356
        m_right->set_cluster(cluster);
22,360✔
4357
    }
22,360✔
4358

4359
    // Recursively fetch tables of columns in expression tree. Used when user first builds a stand-alone expression
4360
    // and
4361
    // binds it to a Query at a later time
4362
    ConstTableRef get_base_table() const override
4363
    {
1,280✔
4364
        ConstTableRef l = m_left->get_base_table();
1,280✔
4365
        ConstTableRef r = m_right->get_base_table();
1,280✔
4366

4367
        // Queries do not support multiple different tables; all tables must be the same.
4368
        REALM_ASSERT(l == nullptr || r == nullptr || l == r);
1,280✔
4369

4370
        // nullptr pointer means expression which isn't yet associated with any table, or is a Value<T>
4371
        return bool(l) ? l : r;
1,280✔
4372
    }
1,280✔
4373

4374
    // destination = operator(left, right)
4375
    void evaluate(Subexpr::Index& index, ValueBase& destination) override
4376
    {
1,726,852✔
4377
        Value<T> result;
1,726,852✔
4378
        Value<T> left;
1,726,852✔
4379
        Value<T> right;
1,726,852✔
4380
        if (m_left_is_const) {
1,726,852✔
4381
            m_right->evaluate(index, right);
488✔
4382
            result.template fun_const<oper>(m_const_value, right);
488✔
4383
        }
488✔
4384
        else if (m_right_is_const) {
1,726,364✔
4385
            m_left->evaluate(index, left);
562,152✔
4386
            result.template fun_const<oper>(left, m_const_value);
562,152✔
4387
        }
562,152✔
4388
        else {
1,164,212✔
4389
            m_left->evaluate(index, left);
1,164,212✔
4390
            m_right->evaluate(index, right);
1,164,212✔
4391
            result.template fun<oper>(left, right);
1,164,212✔
4392
        }
1,164,212✔
4393
        destination = result;
1,726,852✔
4394
    }
1,726,852✔
4395

4396
    std::string description(util::serializer::SerialisationState& state) const override
4397
    {
204✔
4398
        std::string s = "(";
204✔
4399
        if (m_left) {
204✔
4400
            s += m_left->description(state);
204✔
4401
        }
204✔
4402
        s += (" " + oper::description() + " ");
204✔
4403
        if (m_right) {
204✔
4404
            s += m_right->description(state);
204✔
4405
        }
204✔
4406
        s += ")";
204✔
4407
        return s;
204✔
4408
    }
204✔
4409

4410
    util::Optional<ExpressionComparisonType> get_comparison_type() const override
4411
    {
1,482,464✔
4412
        if (!m_left_is_const) {
1,482,464✔
4413
            return m_left->get_comparison_type();
1,482,160✔
4414
        }
1,482,160✔
4415
        if (!m_right_is_const) {
304✔
4416
            return m_right->get_comparison_type();
304✔
4417
        }
304✔
4418
        return util::none;
×
4419
    }
304✔
4420

4421
    std::unique_ptr<Subexpr> clone() const override
4422
    {
1,964✔
4423
        return make_subexpr<Operator>(*this);
1,964✔
4424
    }
1,964✔
4425

4426
private:
4427
    std::unique_ptr<Subexpr> m_left;
4428
    std::unique_ptr<Subexpr> m_right;
4429
    bool m_left_is_const;
4430
    bool m_right_is_const;
4431
    Mixed m_const_value;
4432
};
4433

4434
class CompareBase : public Expression {
4435
public:
4436
    CompareBase(std::unique_ptr<Subexpr> left, std::unique_ptr<Subexpr> right)
4437
        : m_left(std::move(left))
34,512✔
4438
        , m_right(std::move(right))
34,512✔
4439
    {
69,024✔
4440
        if (m_left->has_constant_evaluation()) {
69,024✔
4441
            m_left_const_values = dynamic_cast<ValueBase*>(m_left.get());
27,696✔
4442
        }
27,696✔
4443
        if (m_right->has_constant_evaluation()) {
69,024✔
4444
            m_right_const_values = dynamic_cast<ValueBase*>(m_right.get());
33,300✔
4445
        }
33,300✔
4446
        REALM_ASSERT(!(m_left_const_values && m_right_const_values));
69,024✔
4447
    }
69,024✔
4448

4449
    // See comment in base class
4450
    void set_base_table(ConstTableRef table) override
4451
    {
74,274✔
4452
        m_left->set_base_table(table);
74,274✔
4453
        m_right->set_base_table(table);
74,274✔
4454
    }
74,274✔
4455

4456
    void set_cluster(const Cluster* cluster) override
4457
    {
121,512✔
4458
        if (m_has_matches) {
121,512✔
4459
            m_cluster = cluster;
2,286✔
4460
        }
2,286✔
4461
        else {
119,226✔
4462
            m_left->set_cluster(cluster);
119,226✔
4463
            m_right->set_cluster(cluster);
119,226✔
4464
        }
119,226✔
4465
    }
121,512✔
4466

4467
    // Recursively fetch tables of columns in expression tree. Used when user first builds a stand-alone expression
4468
    // and binds it to a Query at a later time
4469
    ConstTableRef get_base_table() const override
4470
    {
69,024✔
4471
        ConstTableRef l = m_left->get_base_table();
69,024✔
4472
        ConstTableRef r = m_right->get_base_table();
69,024✔
4473

4474
        // All main tables in each subexpression of a query (table.columns() or table.link()) must be the same.
4475
        REALM_ASSERT(l == nullptr || r == nullptr || l == r);
69,024✔
4476

4477
        // nullptr pointer means expression which isn't yet associated with any table, or is a Value<T>
4478
        return (l) ? l : r;
69,024✔
4479
    }
69,024✔
4480

4481
    void collect_dependencies(std::vector<TableKey>& tables) const override
4482
    {
9,234✔
4483
        m_left->collect_dependencies(tables);
9,234✔
4484
        m_right->collect_dependencies(tables);
9,234✔
4485
    }
9,234✔
4486

4487
    size_t find_first_with_matches(size_t start, size_t end) const
4488
    {
20,292✔
4489
        if (m_index_end == 0 || start >= end)
20,292✔
4490
            return not_found;
48✔
4491

4492
        ObjKey first_key = m_cluster->get_real_key(start);
20,244✔
4493
        ObjKey actual_key;
20,244✔
4494

4495
        // Sequential lookup optimization: when the query isn't constrained
4496
        // to a LnkLst we'll get find_first() requests in ascending order,
4497
        // so we can do a simple linear scan.
4498
        if (m_index_get < m_index_end && m_matches[m_index_get] <= first_key) {
20,244✔
4499
            actual_key = m_matches[m_index_get];
16,089✔
4500
            // skip through keys which are in "earlier" leafs than the one selected by start..end:
4501
            while (first_key > actual_key) {
29,991✔
4502
                m_index_get++;
15,429✔
4503
                if (m_index_get == m_index_end)
15,429✔
4504
                    return not_found;
1,527✔
4505
                actual_key = m_matches[m_index_get];
13,902✔
4506
            }
13,902✔
4507
        }
16,089✔
4508
        // Otherwise if we get requests out of order we have to do a more
4509
        // expensive binary search
4510
        else {
4,155✔
4511
            auto it = std::lower_bound(m_matches.begin(), m_matches.end(), first_key);
4,155✔
4512
            if (it == m_matches.end())
4,155✔
4513
                return not_found;
1,680✔
4514
            actual_key = *it;
2,475✔
4515
        }
2,475✔
4516

4517
        // if actual key is bigger than last key, it is not in this leaf
4518
        ObjKey last_key = start + 1 == end ? first_key : m_cluster->get_real_key(end - 1);
17,037✔
4519
        if (actual_key > last_key)
17,037✔
4520
            return not_found;
1,269✔
4521

4522
        // key is known to be in this leaf, so find key whithin leaf keys
4523
        return m_cluster->lower_bound_key(ObjKey(actual_key.value - m_cluster->get_offset()));
15,768✔
4524
    }
17,037✔
4525

4526
protected:
4527
    CompareBase(const CompareBase& other)
4528
        : m_left(other.m_left->clone())
35,160✔
4529
        , m_right(other.m_right->clone())
35,160✔
4530
    {
70,320✔
4531
        if (m_left->has_constant_evaluation()) {
70,320✔
4532
            m_left_const_values = dynamic_cast<ValueBase*>(m_left.get());
21,018✔
4533
        }
21,018✔
4534
        if (m_right->has_constant_evaluation()) {
70,320✔
4535
            m_right_const_values = dynamic_cast<ValueBase*>(m_right.get());
35,316✔
4536
        }
35,316✔
4537
    }
70,320✔
4538

4539
    std::unique_ptr<Subexpr> m_left;
4540
    std::unique_ptr<Subexpr> m_right;
4541
    const Cluster* m_cluster;
4542
    ValueBase* m_left_const_values = nullptr;
4543
    ValueBase* m_right_const_values = nullptr;
4544
    bool m_has_matches = false;
4545
    std::vector<ObjKey> m_matches;
4546
    mutable size_t m_index_get = 0;
4547
    size_t m_index_end = 0;
4548
};
4549

4550
template <class TCond>
4551
class Compare : public CompareBase {
4552
public:
4553
    using CompareBase::CompareBase;
4554

4555
    double init() override
4556
    {
79,612✔
4557
        double dT = 50.0;
79,612✔
4558
        if ((m_left->has_single_value()) || (m_right->has_single_value())) {
79,612✔
4559
            dT = 10.0;
61,366✔
4560
            if constexpr (std::is_same_v<TCond, Equal>) {
61,366✔
4561
                // If the property not being constant has a search index we can speed things up by
4562
                // finding all matches up front.
4563
                Mixed const_value;
37,722✔
4564
                Subexpr* column;
37,722✔
4565
                std::optional<ExpressionComparisonType> const_value_cmp_type;
37,722✔
4566
                if (m_left->has_single_value()) {
37,722✔
4567
                    const_value = m_left->get_mixed();
14,736✔
4568
                    const_value_cmp_type = m_left->get_comparison_type();
14,736✔
4569
                    column = m_right.get();
14,736✔
4570
                }
14,736✔
4571
                else {
22,986✔
4572
                    const_value = m_right->get_mixed();
22,986✔
4573
                    const_value_cmp_type = m_right->get_comparison_type();
22,986✔
4574
                    column = m_left.get();
22,986✔
4575
                }
22,986✔
4576

4577
                if (column->has_search_index() && !column->has_indexes_in_link_map() &&
37,722✔
4578
                    column->get_comparison_type().value_or(ExpressionComparisonType::Any) ==
37,722✔
4579
                        ExpressionComparisonType::Any &&
2,784✔
4580
                    const_value_cmp_type.value_or(ExpressionComparisonType::Any) != ExpressionComparisonType::None) {
37,722✔
4581
                    if (const_value.is_null()) {
2,232✔
4582
                        const ObjPropertyBase* prop = dynamic_cast<const ObjPropertyBase*>(m_right.get());
354✔
4583
                        // when checking for null across links, null links are considered matches,
4584
                        // so we must compute the slow matching even if there is an index.
4585
                        if (!prop || prop->links_exist()) {
354✔
4586
                            return dT;
288✔
4587
                        }
288✔
4588
                        else {
66✔
4589
                            m_matches = column->find_all(Mixed());
66✔
4590
                        }
66✔
4591
                    }
354✔
4592
                    else {
1,878✔
4593
                        if (column->get_type() != const_value.get_type()) {
1,878✔
4594
                            // If the type we are looking for is not the same type as the target
4595
                            // column, we cannot use the index
4596
                            return dT;
30✔
4597
                        }
30✔
4598
                        m_matches = column->find_all(const_value);
1,848✔
4599
                    }
1,848✔
4600
                    // Sort
4601
                    std::sort(m_matches.begin(), m_matches.end());
1,914✔
4602
                    // Remove all duplicates
4603
                    m_matches.erase(std::unique(m_matches.begin(), m_matches.end()), m_matches.end());
1,914✔
4604

4605
                    m_has_matches = true;
1,914✔
4606
                    m_index_get = 0;
1,914✔
4607
                    m_index_end = m_matches.size();
1,914✔
4608
                    dT = 0;
1,914✔
4609
                }
1,914✔
4610
            }
37,722✔
4611
        }
61,366✔
4612

4613
        return dT;
43,029✔
4614
    }
79,612✔
4615

4616
    size_t find_first(size_t start, size_t end) const override
4617
    {
1,755,504✔
4618
        if (m_has_matches) {
1,755,504✔
4619
            return find_first_with_matches(start, end);
20,292✔
4620
        }
20,292✔
4621

4622
        size_t match;
1,735,212✔
4623
        ValueBase left_buf;
1,735,212✔
4624
        ValueBase right_buf;
1,735,212✔
4625
        const util::Optional<ExpressionComparisonType> left_cmp_type = m_left->get_comparison_type();
1,735,212✔
4626
        const util::Optional<ExpressionComparisonType> right_cmp_type = m_right->get_comparison_type();
1,735,212✔
4627

4628
        ValueBase* left = m_left_const_values ? m_left_const_values : &left_buf;
1,735,212✔
4629
        ValueBase* right = m_right_const_values ? m_right_const_values : &right_buf;
1,735,212✔
4630

4631
        for (; start < end;) {
4,073,983✔
4632
            // In case of wildcard query strings, we will get a value for every collection matching the path
4633
            // We need to match those separately against the other value - which might also come in multiple
4634
            // instances.
4635
            Subexpr::Index right_index(start);
3,986,216✔
4636
            do {
3,987,110✔
4637
                Subexpr::Index left_index(start);
3,987,110✔
4638
                if (!m_right_const_values) {
3,987,110✔
4639
                    m_right->evaluate(right_index, right_buf);
3,583,192✔
4640
                }
3,583,192✔
4641
                do {
3,988,994✔
4642
                    if (!m_left_const_values)
3,988,994✔
4643
                        m_left->evaluate(left_index, left_buf);
1,493,765✔
4644
                    match = ValueBase::template compare<TCond>(*left, *right, left_cmp_type, right_cmp_type);
3,988,994✔
4645
                    if (match != not_found && match + start < end)
3,988,994✔
4646
                        return start + match;
1,647,445✔
4647
                } while (left_index.more());
3,988,994✔
4648
            } while (right_index.more());
3,987,110✔
4649

4650
            size_t rows = (left->m_from_list || right->m_from_list) ? 1 : std::min(right->size(), left->size());
2,338,771✔
4651
            start += rows;
2,338,771✔
4652
        }
2,338,771✔
4653

4654
        return not_found; // no match
87,767✔
4655
    }
1,735,212✔
4656

4657
    std::string description(util::serializer::SerialisationState& state) const override
4658
    {
19,942✔
4659
        if constexpr (realm::is_any_v<TCond, BeginsWith, BeginsWithIns, EndsWith, EndsWithIns, Contains, ContainsIns,
9,971✔
4660
                                      Like, LikeIns>) {
10,721✔
4661
            // these string conditions have the arguments reversed but the order is important
4662
            // operations ==, and != can be reversed because the produce the same results both ways
4663
            return util::serializer::print_value(util::format("%1 %2 %3", m_right->description(state),
1,500✔
4664
                                                              TCond::description(), m_left->description(state)));
1,500✔
4665
        }
750✔
4666
        else {
18,442✔
4667
            state.target_table = m_right->get_target_table();
18,442✔
4668
            std::string ret = m_left->description(state) + " " + TCond::description() + " ";
18,442✔
4669
            state.target_table = m_left->get_target_table();
18,442✔
4670
            ret += m_right->description(state);
18,442✔
4671
            return ret;
18,442✔
4672
        }
18,442✔
4673
    }
19,942✔
4674

4675
    std::unique_ptr<Expression> clone() const override
4676
    {
66,494✔
4677
        return std::unique_ptr<Expression>(new Compare(*this));
66,494✔
4678
    }
66,494✔
4679
};
4680
} // namespace realm
4681
#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