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

realm / realm-core / jonathan.reams_3390

31 Jul 2024 07:00PM UTC coverage: 91.105% (-0.01%) from 91.116%
jonathan.reams_3390

Pull #7938

Evergreen

jbreams
RCORE-2212 Make apply-to-state tool handle all batch states properly
Pull Request #7938: RCORE-2212 Make apply-to-state tool handle all batch states properly

102768 of 181570 branches covered (56.6%)

216827 of 237996 relevant lines covered (91.11%)

5898131.93 hits per line

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

93.81
/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,834,507✔
172
    return a < b ? a : b;
3,834,507✔
173
}
3,834,507✔
174

175
struct Plus {
176
    Mixed operator()(Mixed v1, Mixed v2) const
177
    {
7,129,052✔
178
        return v1 + v2;
7,129,052✔
179
    }
7,129,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,780,684✔
189
        return v1 - v2;
3,780,684✔
190
    }
3,780,684✔
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,513,380✔
200
        return v1 / v2;
1,513,380✔
201
    }
1,513,380✔
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,514,184✔
211
        return v1 * v2;
1,514,184✔
212
    }
1,514,184✔
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,910,952✔
249
    ValueBase(const ValueType& init_val)
250
    {
751,557✔
251
        m_first[0] = init_val;
751,557✔
252
    }
751,557✔
253
    ~ValueBase()
254
    {
14,712,192✔
255
        dealloc();
14,712,192✔
256
    }
14,712,192✔
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,728,971✔
264
        init(other.m_from_list, other.size());
2,728,971✔
265
        set(other.begin(), other.end());
2,728,971✔
266
        return *this;
2,728,971✔
267
    }
2,728,971✔
268

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

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

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

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

298
    template <class T>
299
    void set(size_t ndx, const T& val)
300
    {
76,363,253✔
301
        if constexpr (std::is_same<T, float>::value || std::is_same<T, double>::value) {
65,416,441✔
302
            m_first[ndx] = null::is_null_float(val) ? ValueType() : ValueType(val);
40,891,621✔
303
        }
12,811,873✔
304
        else {
75,111,881✔
305
            m_first[ndx] = ValueType(val);
75,111,881✔
306
        }
75,111,881✔
307
    }
76,363,253✔
308

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

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

324
    const ValueType& operator[](size_t n) const
325
    {
23,334,312✔
326
        return m_first[n];
23,334,312✔
327
    }
23,334,312✔
328

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

334
    ValueType* begin()
335
    {
40,308✔
336
        return m_first;
40,308✔
337
    }
40,308✔
338
    const ValueType* begin() const
339
    {
2,729,412✔
340
        return m_first;
2,729,412✔
341
    }
2,729,412✔
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,729,418✔
349
        return m_first + m_size;
2,729,418✔
350
    }
2,729,418✔
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
    {
575,076✔
372
        TOperator o;
575,076✔
373
        // Operate on values one-by-one
374
        size_t sz = left.size();
575,076✔
375
        init(left.m_from_list, sz);
575,076✔
376
        for (size_t i = 0; i < sz; i++) {
5,114,100✔
377
            set(i, o(left[i], const_value));
4,539,024✔
378
        }
4,539,024✔
379
    }
575,076✔
380
    template <class TOperator>
381
    REALM_FORCEINLINE void fun(const ValueBase& left, const ValueBase& right)
382
    {
1,190,060✔
383
        TOperator o;
1,190,060✔
384

385
        if (!left.m_from_list && !right.m_from_list) {
1,190,060✔
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,190,056✔
388
            init(false, min);
1,190,056✔
389

390
            for (size_t i = 0; i < min; i++) {
10,587,218✔
391
                set(i, o(left[i], right[i]));
9,397,162✔
392
            }
9,397,162✔
393
        }
1,190,056✔
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,190,060✔
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,993,352✔
427
        TCond c;
3,993,352✔
428
        using Compare = ExpressionComparisonType;
3,993,352✔
429

430
        if (!left.m_from_list && !right.m_from_list) {
3,993,352✔
431
            // ALL/NONE not supported for non list types
432
            REALM_ASSERT_DEBUG(!left_cmp_type || *left_cmp_type == Compare::Any);
3,644,320!
433
            REALM_ASSERT_DEBUG(!right_cmp_type || *right_cmp_type == Compare::Any);
3,644,320✔
434
            // Compare values one-by-one (one value is one row; no link lists)
435
            size_t min = minimum(left.size(), right.size());
3,644,320✔
436
            for (size_t m = 0; m < min; m++) {
11,473,232✔
437
                if (c(left[m], right[m]))
9,328,289✔
438
                    return m;
1,499,377✔
439
            }
9,328,289✔
440
            return not_found;
2,144,943✔
441
        }
3,644,320✔
442

443
        if (left.m_from_list && right.m_from_list && !left_cmp_type && !right_cmp_type) {
349,032!
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,900✔
463
        const Compare compare_right = right_cmp_type.value_or(Compare::Any);
344,900✔
464

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

468
        if (left_size > 2 && right_size > 2) {
344,900✔
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,348✔
518
                                      Like, LikeIns>) {
184,687✔
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,062✔
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,092✔
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,123✔
562
            auto right_matches = [&](const QueryValue& left_val) {
472,188✔
563
                for (size_t i = 0; i < right_size; i++) {
830,496✔
564
                    if (c(left_val, right[i])) {
500,044✔
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,446✔
574
                        // no match
575
                        if (compare_right == Compare::All) {
355,446✔
576
                            return false;
2,862✔
577
                        }
2,862✔
578
                    }
355,446✔
579
                }
500,044✔
580
                return compare_right == Compare::None || compare_right == Compare::All;
330,452✔
581
            };
472,188✔
582

583
            for (size_t i = 0; i < left_size; i++) {
650,995✔
584
                if (right_matches(left[i])) {
472,194✔
585
                    if (compare_left == Compare::Any) {
143,104✔
586
                        return 0;
115,656✔
587
                    }
115,656✔
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,090✔
593
                    if (compare_left == ExpressionComparisonType::All) {
329,090✔
594
                        return not_found;
14,014✔
595
                    }
14,014✔
596
                }
329,090✔
597
            }
472,194✔
598
            if (compare_left == ExpressionComparisonType::None || compare_left == ExpressionComparisonType::All) {
178,801✔
599
                return 0; // either none or all
28,584✔
600
            }
28,584✔
601
        }
178,801✔
602

603
        return not_found; // no match
165,517✔
604
    }
300,984✔
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,526,552✔
618
        if (size == m_size)
14,526,552✔
619
            return;
6,172,119✔
620

621
        dealloc();
8,354,433✔
622
        m_size = size;
8,354,433✔
623
        if (m_size > 0) {
8,354,433✔
624
            if (m_size > prealloc)
8,143,419✔
625
                m_first = new QueryValue[m_size];
15,534✔
626
            else
8,127,885✔
627
                m_first = &m_cache[0];
8,127,885✔
628
        }
8,143,419✔
629
    }
8,354,433✔
630
    void dealloc()
631
    {
23,066,598✔
632
        if (m_first) {
23,066,598✔
633
            if (m_size > prealloc)
22,854,483✔
634
                delete[] m_first;
15,534✔
635
            m_first = nullptr;
22,854,483✔
636
        }
22,854,483✔
637
    }
23,066,598✔
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,165,671✔
681
        {
8,391,684✔
682
        }
8,391,684✔
683
        bool initialize() const
684
        {
252,990✔
685
            return sub_index == 0;
252,990✔
686
        }
252,990✔
687
        operator size_t() const
688
        {
7,495,746✔
689
            return row_index;
7,495,746✔
690
        }
7,495,746✔
691
        explicit operator bool() = delete;
692
        bool more() const
693
        {
4,862,214✔
694
            return sub_index < sub_size;
4,862,214✔
695
        }
4,862,214✔
696
        bool set_size(size_t s)
697
        {
249,630✔
698
            sub_size = s;
249,630✔
699
            return sub_size != 0;
249,630✔
700
        }
249,630✔
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;
12,051,132✔
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*) {}
92,454✔
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,466✔
745
        return false;
104,466✔
746
    }
104,466✔
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,428✔
784
        return util::none;
979,428✔
785
    }
979,428✔
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,865,262✔
1049
        return ColumnTypeTraits<T>::id;
1,865,262✔
1050
    }
1,865,262✔
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,494,234✔
1265

1266
    Value(T init)
1267
        : ValueBase(QueryValue(init))
265,518✔
1268
    {
531,036✔
1269
    }
531,036✔
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
    {
559,732✔
1335
        return size() == 1;
559,732✔
1336
    }
559,732✔
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,006✔
1345
        REALM_ASSERT_DEBUG(!m_comparison_type || m_from_list);
1,479,006!
1346
        return m_comparison_type;
1,479,006✔
1347
    }
1,479,006✔
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,062✔
1356
        return get(0);
511,062✔
1357
    }
511,062✔
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,914✔
1378
        begin()->use_buffer(m_buffer);
22,914✔
1379
    }
22,914✔
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,516✔
1540
    {
543,060✔
1541
        set_base_table(table);
543,060✔
1542
    }
543,060✔
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,954,365✔
1559
        return m_link_column_keys.size() > 0;
7,954,365✔
1560
    }
7,954,365✔
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,890✔
1620
        std::vector<ObjKey> res;
247,890✔
1621
        get_links(index, res);
247,890✔
1622
        return res;
247,890✔
1623
    }
247,890✔
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,342✔
1650
        map_links(0, row, lm);
366,342✔
1651
    }
366,342✔
1652

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

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

1663
    ConstTableRef get_target_table() const
1664
    {
2,577,129✔
1665
        REALM_ASSERT(!m_tables.empty());
2,577,129✔
1666
        return m_tables.back();
2,577,129✔
1667
    }
2,577,129✔
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,960✔
1689
            result.push_back(key);
357,960✔
1690
            return true; // continue evaluation
357,960✔
1691
        });
357,960✔
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,316✔
1726
        , m_column_key(column)
255,316✔
1727
        , m_comparison_type(type)
255,316✔
1728
    {
510,652✔
1729
    }
510,652✔
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,752,761✔
1745
        return m_link_map.has_links();
5,752,761✔
1746
    }
5,752,761✔
1747

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

1753
    bool is_nullable() const
1754
    {
3,034,022✔
1755
        return m_column_key.get_attrs().test(col_attr_Nullable);
3,034,022✔
1756
    }
3,034,022✔
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,301✔
1765
        return m_column_key;
728,301✔
1766
    }
728,301✔
1767

1768
    virtual bool has_path() const noexcept
1769
    {
469,776✔
1770
        return false;
469,776✔
1771
    }
469,776✔
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
    {
170,264✔
1794
        return m_link_map.get_base_table();
170,264✔
1795
    }
170,264✔
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,120✔
1842
            auto ndxs = m_link_map.get_origin_objkeys(k);
10,120✔
1843
            ret.insert(ret.end(), ndxs.begin(), ndxs.end());
10,120✔
1844
        }
10,120✔
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
    {
778,336✔
1861
        return m_comparison_type;
778,336✔
1862
    }
778,336✔
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
private:
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
        m_ctrl.alloc = &get_link_map().get_target_table()->get_alloc();
1,612✔
2057
        m_ctrl.group = table->get_parent_group();
1,612✔
2058
    }
1,612✔
2059

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

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

2109
private:
2110
    Collection::QueryCtrlBlock m_ctrl;
2111
};
2112

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

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

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

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

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

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

2159
    Columns(Columns&& other) noexcept
2160
        : SimpleQuerySupport(other)
2161
    {
×
2162
    }
×
2163

2164
    Query fulltext(StringData sd) const;
2165

2166
    using SimpleQuerySupport::size;
2167

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

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

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

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

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

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

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

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

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

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

2247

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

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

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

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

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

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

2295
        return not_found;
130✔
2296
    }
1,574✔
2297

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

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

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

2315
    mutable LinkMap m_link_map;
2316
};
2317

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

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

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

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

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

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

2359
    void evaluate(Subexpr::Index& index, ValueBase& destination) override;
2360

2361
    std::string description(util::serializer::SerialisationState& state) const override;
2362

2363
private:
2364
    LinkMap m_link_map;
2365
    ColKey m_column_key;
2366
};
2367

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

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

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

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

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

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

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

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

2444
private:
2445
    const Cluster* m_cluster = nullptr;
2446
    LinkMap m_link_map;
2447
};
2448

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

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

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

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

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

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

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

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

2551
        return not_found;
272✔
2552
    }
896✔
2553

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

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

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

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

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

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

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

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

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

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

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

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

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

2666
private:
2667
    std::unique_ptr<Subexpr> m_expr;
2668
};
2669

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

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

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

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

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

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

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

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

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

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

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

2734
private:
2735
    std::unique_ptr<Subexpr> m_expr;
2736
};
2737

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

2745
    void set_base_table(ConstTableRef) override {}
216✔
2746

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

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

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

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

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

2773
    ObjKey m_key;
2774
};
2775

2776
template <typename T>
2777
class SubColumns;
2778

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

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

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

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

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

2823
    LinkCount count() const
2824
    {
852✔
2825
        return LinkCount(m_link_map);
852✔
2826
    }
852✔
2827

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

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

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

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

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

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

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

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

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

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

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

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

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

2900
    void evaluate(Subexpr::Index& index, ValueBase& destination) override;
2901

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

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

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

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

2942
    void set_cluster(const Cluster* cluster);
2943

2944
    void get_lists(size_t index, Value<int64_t>& destination);
2945

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

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

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

2963
    virtual bool index(const PathElement&) = 0;
2964

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

2971
template <typename>
2972
class ColumnListSize;
2973

2974
template <typename T>
2975
class ColumnListElementLength;
2976

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

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

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

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

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

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

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

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

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

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

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

3055
    SizeOperator<int64_t> size() override;
3056

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

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

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

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

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

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

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

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

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

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

3153
protected:
3154
    Value<int64_t> m_list_refs;
3155

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

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

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

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

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

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

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

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

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

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

3236
        return ret;
572✔
3237
    }
572✔
3238
};
3239

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

3254

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

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

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

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

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

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

3323
                do {
80✔
3324
                    Value<Mixed> destination;
80✔
3325
                    ColumnsCollection<Mixed>::evaluate<Mixed>(m_sub_index, destination);
80✔
3326

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

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

3351
private:
3352
    Collection::QueryCtrlBlock m_ctrl;
3353
    Subexpr::Index m_sub_index = 0;
3354
};
3355

3356
// Returns the keys
3357
class ColumnDictionaryKeys;
3358

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

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

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

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

3400
    DataType get_key_type() const
3401
    {
90✔
3402
        return m_key_type;
90✔
3403
    }
90✔
3404

3405
    ColumnDictionaryKeys keys();
3406

3407
    void set_base_table(ConstTableRef table) override
3408
    {
984✔
3409
        ColumnsCollection::set_base_table(table);
984✔
3410
        m_ctrl.alloc = &m_link_map.get_target_table()->get_alloc();
984✔
3411
        m_ctrl.group = table->get_parent_group();
984✔
3412
    }
984✔
3413
    SizeOperator<int64_t> size() override;
3414
    std::unique_ptr<Subexpr> get_element_length() override
3415
    {
×
3416
        // Not supported for Dictionary
3417
        return {};
×
3418
    }
×
3419

3420
    void evaluate(Subexpr::Index& index, ValueBase& destination) override;
3421

3422
    std::string description(util::serializer::SerialisationState& state) const override
3423
    {
138✔
3424
        return ColumnListBase::description(state) + util::to_string(m_ctrl.path);
138✔
3425
    }
138✔
3426

3427
    std::unique_ptr<Subexpr> clone() const override
3428
    {
1,128✔
3429
        return make_subexpr<Columns<Dictionary>>(*this);
1,128✔
3430
    }
1,128✔
3431

3432
    bool index(const PathElement&) override
3433
    {
×
3434
        return false;
×
3435
    }
×
3436

3437
    Columns(Columns const& other)
3438
        : ColumnsCollection<Mixed>(other)
582✔
3439
        , m_key_type(other.m_key_type)
582✔
3440
        , m_ctrl(other.m_ctrl)
582✔
3441
    {
1,164✔
3442
    }
1,164✔
3443

3444
protected:
3445
    DataType m_key_type = type_String;
3446
    Collection::QueryCtrlBlock m_ctrl;
3447

3448
    void init_path(const PathElement* begin, const PathElement* end);
3449
};
3450

3451
// Returns the keys
3452
class ColumnDictionaryKeys : public Subexpr2<StringData> {
3453
public:
3454
    ColumnDictionaryKeys(const Columns<Dictionary>& dict)
3455
        : m_key_type(dict.get_key_type())
45✔
3456
        , m_column_key(dict.m_column_key)
45✔
3457
        , m_link_map(dict.m_link_map)
45✔
3458
        , m_comparison_type(dict.get_comparison_type())
45✔
3459
    {
90✔
3460
        REALM_ASSERT(m_key_type == type_String);
90✔
3461
    }
90✔
3462

3463
    ConstTableRef get_base_table() const final
3464
    {
6,696✔
3465
        return m_link_map.get_base_table();
6,696✔
3466
    }
6,696✔
3467

3468
    void set_base_table(ConstTableRef table) final
3469
    {
90✔
3470
        m_link_map.set_base_table(table);
90✔
3471
    }
90✔
3472

3473
    void collect_dependencies(std::vector<TableKey>& tables) const final
3474
    {
12✔
3475
        m_link_map.collect_dependencies(tables);
12✔
3476
    }
12✔
3477

3478
    util::Optional<ExpressionComparisonType> get_comparison_type() const final
3479
    {
2,082✔
3480
        return m_comparison_type;
2,082✔
3481
    }
2,082✔
3482

3483
    void set_cluster(const Cluster* cluster) override;
3484
    void evaluate(Subexpr::Index& index, ValueBase& destination) override;
3485

3486
    std::string description(util::serializer::SerialisationState& state) const override
3487
    {
30✔
3488
        return state.describe_expression_type(m_comparison_type) + state.describe_columns(m_link_map, m_column_key) +
30✔
3489
               ".@keys";
30✔
3490
    }
30✔
3491

3492
    std::unique_ptr<Subexpr> clone() const override
3493
    {
102✔
3494
        return std::unique_ptr<Subexpr>(new ColumnDictionaryKeys(*this));
102✔
3495
    }
102✔
3496

3497
    ColumnDictionaryKeys(const ColumnDictionaryKeys& other)
3498
        : m_key_type(other.m_key_type)
51✔
3499
        , m_column_key(other.m_column_key)
51✔
3500
        , m_link_map(other.m_link_map)
51✔
3501
        , m_comparison_type(other.m_comparison_type)
51✔
3502
    {
102✔
3503
    }
102✔
3504

3505
private:
3506
    DataType m_key_type;
3507
    ColKey m_column_key;
3508
    LinkMap m_link_map;
3509
    std::optional<ExpressionComparisonType> m_comparison_type;
3510
    std::optional<ArrayInteger> m_leaf;
3511
    std::vector<ObjKey> m_links;
3512
};
3513

3514
template <typename T>
3515
class ColumnListSize : public ColumnsCollection<T> {
3516
public:
3517
    ColumnListSize(const ColumnsCollection<T>& other)
3518
        : ColumnsCollection<T>(other)
1,786✔
3519
    {
3,572✔
3520
    }
3,572✔
3521

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

3533
    std::unique_ptr<Subexpr> clone() const override
3534
    {
7,014✔
3535
        return std::unique_ptr<Subexpr>(new ColumnListSize<T>(*this));
7,014✔
3536
    }
7,014✔
3537

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

3561

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

3613
    void set_base_table(ConstTableRef table) override
3614
    {
1,652✔
3615
        m_list.set_base_table(table);
1,652✔
3616
    }
1,652✔
3617

3618
    void set_cluster(const Cluster* cluster) override
3619
    {
1,652✔
3620
        m_list.set_cluster(cluster);
1,652✔
3621
    }
1,652✔
3622

3623
    void collect_dependencies(std::vector<TableKey>& tables) const override
3624
    {
×
3625
        m_list.collect_dependencies(tables);
×
3626
    }
×
3627

3628
    std::unique_ptr<Subexpr> clone() const override
3629
    {
3,304✔
3630
        return std::unique_ptr<Subexpr>(new ColumnListElementLength<T>(*this));
3,304✔
3631
    }
3,304✔
3632

3633
    std::string description(util::serializer::SerialisationState& state) const override
3634
    {
826✔
3635
        return m_list.description(state) + util::serializer::value_separator + "length";
826✔
3636
    }
826✔
3637

3638
    util::Optional<ExpressionComparisonType> get_comparison_type() const override
3639
    {
5,300✔
3640
        return m_list.get_comparison_type();
5,300✔
3641
    }
5,300✔
3642

3643
private:
3644
    ColumnsCollection<T> m_list;
3645
};
3646

3647

3648
template <typename T>
3649
SizeOperator<int64_t> ColumnsCollection<T>::size()
3650
{
3,572✔
3651
    std::unique_ptr<Subexpr> ptr(new ColumnListSize<T>(*this));
3,572✔
3652
    return SizeOperator<int64_t>(std::move(ptr));
3,572✔
3653
}
3,572✔
3654

3655
template <typename T, typename Operation>
3656
class CollectionColumnAggregate : public Subexpr2<decltype(Operation().result())> {
3657
public:
3658
    CollectionColumnAggregate(ColumnsCollection<T> column)
3659
        : m_columns_collection(std::move(column))
2,790✔
3660
    {
5,580✔
3661
        if (m_columns_collection.m_column_key.is_dictionary()) {
5,580✔
3662
            m_dictionary_key_type = m_columns_collection.m_link_map.get_target_table()->get_dictionary_key_type(
1,104✔
3663
                m_columns_collection.m_column_key);
1,104✔
3664
        }
1,104✔
3665
    }
5,580✔
3666

3667
    CollectionColumnAggregate(const CollectionColumnAggregate& other)
3668
        : m_columns_collection(other.m_columns_collection)
5,434✔
3669
        , m_dictionary_key_type(other.m_dictionary_key_type)
5,434✔
3670
    {
10,868✔
3671
    }
10,868✔
3672

3673
    DataType get_type() const override
3674
    {
5,952✔
3675
        return DataType(m_columns_collection.m_column_key.get_type());
5,952✔
3676
    }
5,952✔
3677

3678
    std::unique_ptr<Subexpr> clone() const override
3679
    {
10,868✔
3680
        return make_subexpr<CollectionColumnAggregate>(*this);
10,868✔
3681
    }
10,868✔
3682

3683
    ConstTableRef get_base_table() const override
3684
    {
5,580✔
3685
        return m_columns_collection.get_base_table();
5,580✔
3686
    }
5,580✔
3687

3688
    void set_base_table(ConstTableRef table) override
3689
    {
6,060✔
3690
        m_columns_collection.set_base_table(table);
6,060✔
3691
    }
6,060✔
3692

3693
    void set_cluster(const Cluster* cluster) override
3694
    {
10,396✔
3695
        m_columns_collection.set_cluster(cluster);
10,396✔
3696
    }
10,396✔
3697

3698
    void collect_dependencies(std::vector<TableKey>& tables) const override
3699
    {
488✔
3700
        m_columns_collection.collect_dependencies(tables);
488✔
3701
    }
488✔
3702

3703
    void evaluate(Subexpr::Index& index, ValueBase& destination) override
3704
    {
1,132,576✔
3705
        if (m_dictionary_key_type) {
1,132,576✔
3706
            if (m_columns_collection.links_exist()) {
17,508!
3707
                std::vector<ObjKey> links = m_columns_collection.m_link_map.get_links(index);
1,212✔
3708
                auto sz = links.size();
1,212✔
3709

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

3770
    std::string description(util::serializer::SerialisationState& state) const override
3771
    {
1,584✔
3772
        return m_columns_collection.description(state) + util::serializer::value_separator + Operation::description();
1,584✔
3773
    }
1,584✔
3774

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

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

3812
    inline void set_value_for_empty_dictionary(ValueBase& destination, size_t ndx)
3813
    {
1,212✔
3814
        if constexpr (std::is_same_v<Operation, aggregate_operations::Sum<Mixed>>) {
1,212✔
3815
            destination.set(ndx, 0); // the sum of nothing is zero
606✔
3816
        }
414✔
3817
        else {
828✔
3818
            destination.set_null(ndx);
828✔
3819
        }
828✔
3820
    }
1,212✔
3821

3822
    ColumnsCollection<T> m_columns_collection;
3823
    util::Optional<DataType> m_dictionary_key_type;
3824
};
3825

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

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

3862

3863
template <class T>
3864
class Columns : public ObjPropertyExpr<T> {
3865
    constexpr static bool requires_null_column = realm::is_any_v<T, int64_t, bool>;
3866

3867
public:
3868
    using LeafType = typename ColumnTypeTraits<T>::cluster_leaf_type;
3869
    using NullableLeafType =
3870
        std::conditional_t<requires_null_column, typename ColumnTypeTraits<util::Optional<T>>::cluster_leaf_type,
3871
                           LeafType>;
3872
    using ObjPropertyExpr<T>::links_exist;
3873
    using ObjPropertyBase::is_nullable;
3874

3875
    Columns(ColKey column, ConstTableRef table, const std::vector<ExtendedColumnKey>& links = {},
3876
            util::Optional<ExpressionComparisonType> type = util::none)
3877
        : ObjPropertyExpr<T>(column, table, links, type)
22,888✔
3878
    {
45,796✔
3879
    }
45,796✔
3880

3881
    Columns(const Columns& other)
3882
        : ObjPropertyExpr<T>(other)
17,264✔
3883
    {
34,528✔
3884
    }
34,528✔
3885

3886
    void set_cluster(const Cluster* cluster) override
3887
    {
65,980✔
3888
        if (links_exist()) {
65,980✔
3889
            m_link_map.set_cluster(cluster);
2,432✔
3890
            m_leaf = mpark::monostate();
2,432✔
3891
        }
2,432✔
3892
        else if (requires_null_column && is_nullable()) {
63,548✔
3893
            auto& leaf = m_leaf.template emplace<NullableLeafType>(this->get_base_table()->get_alloc());
11,964✔
3894
            cluster->init_leaf(m_column_key, &leaf);
11,964✔
3895
        }
11,964✔
3896
        else {
51,584✔
3897
            auto& leaf = m_leaf.template emplace<LeafType>(this->get_base_table()->get_alloc());
51,584✔
3898
            cluster->init_leaf(m_column_key, &leaf);
51,584✔
3899
        }
51,584✔
3900
    }
65,980✔
3901

3902
    template <class LeafType2 = LeafType>
3903
    void evaluate_internal(size_t index, ValueBase& destination)
3904
    {
2,946,072✔
3905
        using U = typename LeafType2::value_type;
2,946,072✔
3906

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

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

3938
            size_t rows = std::min(colsize - index, ValueBase::chunk_size);
2,876,360✔
3939

3940
            // Now load `ValueBase::chunk_size` rows from from the leaf into m_storage.
3941
            if constexpr (std::is_same_v<U, int64_t>) {
2,876,360✔
3942
                // If it's an integer leaf, then it contains the method get_chunk() which copies
3943
                // these values in a super fast way. If you want to modify 'chunk_size' then update Array::get_chunk()
3944
                REALM_ASSERT_3(ValueBase::chunk_size, ==, 8);
2,005,976✔
3945

3946
                int64_t res[ValueBase::chunk_size];
2,005,976✔
3947
                static_cast<const Array*>(leaf)->get_chunk(index, res);
2,005,976✔
3948
                destination.init(false, rows);
2,005,976✔
3949
                destination.set(res, res + rows);
2,005,976✔
3950
                return;
2,005,976✔
3951
            }
2,005,976✔
3952

3953
            destination.init(false, rows);
×
3954
            for (size_t t = 0; t < rows; t++) {
8,594,952✔
3955
                if (leaf->is_null(index + t)) {
6,695,132✔
3956
                    destination.set_null(t);
780,504✔
3957
                }
780,504✔
3958
                else {
5,914,628✔
3959
                    destination.set(t, leaf->get(index + t));
5,914,628✔
3960
                }
5,914,628✔
3961
            }
6,695,132✔
3962
        }
1,357,158✔
3963
    }
2,946,072✔
3964

3965
    std::string description(util::serializer::SerialisationState& state) const override
3966
    {
2,412✔
3967
        return state.describe_expression_type(this->m_comparison_type) +
2,412✔
3968
               state.describe_columns(m_link_map, m_column_key);
2,412✔
3969
    }
2,412✔
3970

3971
    // Load values from Column into destination
3972
    void evaluate(Subexpr::Index& index, ValueBase& destination) override
3973
    {
2,946,052✔
3974
        if (is_nullable()) {
2,946,052✔
3975
            evaluate_internal<NullableLeafType>(index, destination);
837,642✔
3976
        }
837,642✔
3977
        else {
2,108,410✔
3978
            evaluate_internal<LeafType>(index, destination);
2,108,410✔
3979
        }
2,108,410✔
3980
    }
2,946,052✔
3981

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

3995
private:
3996
    using ObjPropertyExpr<T>::m_link_map;
3997
    using ObjPropertyExpr<T>::m_column_key;
3998
    using LeafStorage =
3999
        std::conditional_t<requires_null_column, mpark::variant<mpark::monostate, LeafType, NullableLeafType>,
4000
                           mpark::variant<mpark::monostate, LeafType>>;
4001
    LeafStorage m_leaf;
4002
};
4003

4004
template <typename T, typename Operation>
4005
class SubColumnAggregate;
4006

4007
// Defines a uniform interface for aggregation methods.
4008
class SubColumnBase {
4009
public:
4010
    virtual std::unique_ptr<Subexpr> max_of() = 0;
4011
    virtual std::unique_ptr<Subexpr> min_of() = 0;
4012
    virtual std::unique_ptr<Subexpr> sum_of() = 0;
4013
    virtual std::unique_ptr<Subexpr> avg_of() = 0;
4014
};
4015

4016
template <typename T>
4017
class SubColumns : public Subexpr, public SubColumnBase {
4018
public:
4019
    SubColumns(Columns<T>&& column, const LinkMap& link_map)
4020
        : m_column(std::move(column))
1,514✔
4021
        , m_link_map(link_map)
1,514✔
4022
    {
3,028✔
4023
    }
3,028✔
4024

4025
    DataType get_type() const final
4026
    {
×
4027
        return ColumnTypeTraits<T>::id;
×
4028
    }
×
4029

4030
    std::unique_ptr<Subexpr> clone() const override
4031
    {
752✔
4032
        return make_subexpr<SubColumns<T>>(*this);
752✔
4033
    }
752✔
4034

4035
    ConstTableRef get_base_table() const override
4036
    {
×
4037
        return m_link_map.get_base_table();
×
4038
    }
×
4039

4040
    void set_base_table(ConstTableRef table) override
4041
    {
×
4042
        m_link_map.set_base_table(table);
×
4043
        m_column.set_base_table(m_link_map.get_target_table());
×
4044
    }
×
4045

4046
    void collect_dependencies(std::vector<TableKey>& tables) const override
4047
    {
×
4048
        m_link_map.collect_dependencies(tables);
×
4049
    }
×
4050

4051
    void evaluate(Subexpr::Index&, ValueBase&) override
4052
    {
×
4053
        // SubColumns can only be used in an expression in conjunction with its aggregate methods.
4054
        REALM_ASSERT(false);
×
4055
    }
×
4056

4057
    std::string description(util::serializer::SerialisationState&) const override
4058
    {
×
4059
        return ""; // by itself there are no conditions, see SubColumnAggregate
×
4060
    }
×
4061

4062
    SubColumnAggregate<T, aggregate_operations::Minimum<T>> min() const
4063
    {
760✔
4064
        return {m_column, m_link_map};
760✔
4065
    }
760✔
4066

4067
    SubColumnAggregate<T, aggregate_operations::Maximum<T>> max() const
4068
    {
784✔
4069
        return {m_column, m_link_map};
784✔
4070
    }
784✔
4071

4072
    SubColumnAggregate<T, aggregate_operations::Sum<T>> sum() const
4073
    {
712✔
4074
        return {m_column, m_link_map};
712✔
4075
    }
712✔
4076

4077
    SubColumnAggregate<T, aggregate_operations::Average<T>> average() const
4078
    {
764✔
4079
        return {m_column, m_link_map};
764✔
4080
    }
764✔
4081

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

4119
private:
4120
    Columns<T> m_column;
4121
    LinkMap m_link_map;
4122
};
4123

4124
template <typename T, typename Operation>
4125
class SubColumnAggregate : public Subexpr2<decltype(Operation().result())> {
4126
public:
4127
    SubColumnAggregate(const Columns<T>& column, const LinkMap& link_map)
4128
        : m_column(column)
1,510✔
4129
        , m_link_map(link_map)
1,510✔
4130
    {
3,020✔
4131
    }
3,020✔
4132
    SubColumnAggregate(SubColumnAggregate const& other)
4133
        : m_column(other.m_column)
2,932✔
4134
        , m_link_map(other.m_link_map)
2,932✔
4135
    {
5,864✔
4136
    }
5,864✔
4137

4138
    std::unique_ptr<Subexpr> clone() const override
4139
    {
5,864✔
4140
        return make_subexpr<SubColumnAggregate>(*this);
5,864✔
4141
    }
5,864✔
4142

4143
    ConstTableRef get_base_table() const override
4144
    {
3,020✔
4145
        return m_link_map.get_base_table();
3,020✔
4146
    }
3,020✔
4147

4148
    void set_base_table(ConstTableRef table) override
4149
    {
3,500✔
4150
        m_link_map.set_base_table(table);
3,500✔
4151
        m_column.set_base_table(m_link_map.get_target_table());
3,500✔
4152
    }
3,500✔
4153

4154
    void set_cluster(const Cluster* cluster) override
4155
    {
4,412✔
4156
        m_link_map.set_cluster(cluster);
4,412✔
4157
    }
4,412✔
4158

4159
    void collect_dependencies(std::vector<TableKey>& tables) const override
4160
    {
436✔
4161
        m_link_map.collect_dependencies(tables);
436✔
4162
    }
436✔
4163

4164
    void evaluate(Subexpr::Index& index, ValueBase& destination) override
4165
    {
59,268✔
4166
        std::vector<ObjKey> keys = m_link_map.get_links(index);
59,268✔
4167
        std::sort(keys.begin(), keys.end());
59,268✔
4168

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

4186
    std::string description(util::serializer::SerialisationState& state) const override
4187
    {
372✔
4188
        util::serializer::SerialisationState empty_state(state.group);
372✔
4189
        return state.describe_columns(m_link_map, ColKey()) + util::serializer::value_separator +
372✔
4190
               Operation::description() + util::serializer::value_separator + m_column.description(empty_state);
372✔
4191
    }
372✔
4192

4193
private:
4194
    Columns<T> m_column;
4195
    LinkMap m_link_map;
4196
};
4197

4198
class SubQueryCount : public Subexpr2<Int> {
4199
public:
4200
    SubQueryCount(const Query& q, const LinkMap& link_map)
4201
        : m_query(q)
206✔
4202
        , m_link_map(link_map)
206✔
4203
    {
412✔
4204
        REALM_ASSERT(q.produces_results_in_table_order());
412✔
4205
        REALM_ASSERT(m_query.get_table() == m_link_map.get_target_table());
412✔
4206
    }
412✔
4207

4208
    ConstTableRef get_base_table() const override
4209
    {
412✔
4210
        return m_link_map.get_base_table();
412✔
4211
    }
412✔
4212

4213
    void set_base_table(ConstTableRef table) override
4214
    {
440✔
4215
        m_link_map.set_base_table(table);
440✔
4216
        m_query.set_table(m_link_map.get_target_table().cast_away_const());
440✔
4217
    }
440✔
4218

4219
    void set_cluster(const Cluster* cluster) override
4220
    {
588✔
4221
        m_link_map.set_cluster(cluster);
588✔
4222
    }
588✔
4223

4224
    void collect_dependencies(std::vector<TableKey>& tables) const override
4225
    {
32✔
4226
        m_link_map.collect_dependencies(tables);
32✔
4227
    }
32✔
4228

4229
    void evaluate(Subexpr::Index& index, ValueBase& destination) override
4230
    {
3,928✔
4231
        std::vector<ObjKey> links = m_link_map.get_links(index);
3,928✔
4232
        // std::sort(links.begin(), links.end());
4233
        if (!m_initialized) {
3,928✔
4234
            m_query.init();
452✔
4235
            m_initialized = true;
452✔
4236
        }
452✔
4237

4238
        size_t count = std::accumulate(links.begin(), links.end(), size_t(0), [this](size_t running_count, ObjKey k) {
6,988✔
4239
            const Obj obj = m_link_map.get_target_table()->get_object(k);
6,988✔
4240
            return running_count + m_query.eval_object(obj);
6,988✔
4241
        });
6,988✔
4242

4243
        destination = Value<int64_t>(count);
3,928✔
4244
    }
3,928✔
4245

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

4258
    std::unique_ptr<Subexpr> clone() const override
4259
    {
560✔
4260
        return make_subexpr<SubQueryCount>(*this);
560✔
4261
    }
560✔
4262

4263
    SubQueryCount(const SubQueryCount& other)
4264
        : m_query(other.m_query)
280✔
4265
        , m_link_map(other.m_link_map)
280✔
4266
        , m_initialized(false)
280✔
4267
    {
560✔
4268
    }
560✔
4269

4270
private:
4271
    Query m_query;
4272
    LinkMap m_link_map;
4273
    bool m_initialized = false;
4274
};
4275

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

4287
    SubQueryCount count() const
4288
    {
66✔
4289
        return SubQueryCount(m_query, m_link_map);
66✔
4290
    }
66✔
4291

4292
private:
4293
    Query m_query;
4294
    LinkMap m_link_map;
4295
};
4296

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

4314
    Operator(const Operator& other)
4315
        : m_left(other.m_left->clone())
982✔
4316
        , m_right(other.m_right->clone())
982✔
4317
        , m_left_is_const(other.m_left_is_const)
982✔
4318
        , m_right_is_const(other.m_right_is_const)
982✔
4319
        , m_const_value(other.m_const_value)
982✔
4320
    {
1,964✔
4321
    }
1,964✔
4322

4323
    Operator& operator=(const Operator& other)
4324
    {
4325
        if (this != &other) {
4326
            m_left = other.m_left->clone();
4327
            m_right = other.m_right->clone();
4328
        }
4329
        return *this;
4330
    }
4331

4332
    Operator(Operator&&) noexcept = delete;
4333
    Operator& operator=(Operator&&) noexcept = delete;
4334

4335
    DataType get_type() const override
4336
    {
4,568✔
4337
        return m_left->get_type();
4,568✔
4338
    }
4,568✔
4339

4340
    // See comment in base class
4341
    void set_base_table(ConstTableRef table) override
4342
    {
1,280✔
4343
        m_left->set_base_table(table);
1,280✔
4344
        m_right->set_base_table(table);
1,280✔
4345
    }
1,280✔
4346

4347
    void set_cluster(const Cluster* cluster) override
4348
    {
23,584✔
4349
        m_left->set_cluster(cluster);
23,584✔
4350
        m_right->set_cluster(cluster);
23,584✔
4351
    }
23,584✔
4352

4353
    // Recursively fetch tables of columns in expression tree. Used when user first builds a stand-alone expression
4354
    // and
4355
    // binds it to a Query at a later time
4356
    ConstTableRef get_base_table() const override
4357
    {
1,280✔
4358
        ConstTableRef l = m_left->get_base_table();
1,280✔
4359
        ConstTableRef r = m_right->get_base_table();
1,280✔
4360

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

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

4368
    // destination = operator(left, right)
4369
    void evaluate(Subexpr::Index& index, ValueBase& destination) override
4370
    {
1,765,624✔
4371
        Value<T> result;
1,765,624✔
4372
        Value<T> left;
1,765,624✔
4373
        Value<T> right;
1,765,624✔
4374
        if (m_left_is_const) {
1,765,624✔
4375
            m_right->evaluate(index, right);
488✔
4376
            result.template fun_const<oper>(m_const_value, right);
488✔
4377
        }
488✔
4378
        else if (m_right_is_const) {
1,765,136✔
4379
            m_left->evaluate(index, left);
575,076✔
4380
            result.template fun_const<oper>(left, m_const_value);
575,076✔
4381
        }
575,076✔
4382
        else {
1,190,060✔
4383
            m_left->evaluate(index, left);
1,190,060✔
4384
            m_right->evaluate(index, right);
1,190,060✔
4385
            result.template fun<oper>(left, right);
1,190,060✔
4386
        }
1,190,060✔
4387
        destination = result;
1,765,624✔
4388
    }
1,765,624✔
4389

4390
    std::string description(util::serializer::SerialisationState& state) const override
4391
    {
204✔
4392
        std::string s = "(";
204✔
4393
        if (m_left) {
204✔
4394
            s += m_left->description(state);
204✔
4395
        }
204✔
4396
        s += (" " + oper::description() + " ");
204✔
4397
        if (m_right) {
204✔
4398
            s += m_right->description(state);
204✔
4399
        }
204✔
4400
        s += ")";
204✔
4401
        return s;
204✔
4402
    }
204✔
4403

4404
    util::Optional<ExpressionComparisonType> get_comparison_type() const override
4405
    {
1,516,448✔
4406
        if (!m_left_is_const) {
1,516,448✔
4407
            return m_left->get_comparison_type();
1,516,144✔
4408
        }
1,516,144✔
4409
        if (!m_right_is_const) {
304✔
4410
            return m_right->get_comparison_type();
304✔
4411
        }
304✔
4412
        return util::none;
×
4413
    }
304✔
4414

4415
    std::unique_ptr<Subexpr> clone() const override
4416
    {
1,964✔
4417
        return make_subexpr<Operator>(*this);
1,964✔
4418
    }
1,964✔
4419

4420
private:
4421
    std::unique_ptr<Subexpr> m_left;
4422
    std::unique_ptr<Subexpr> m_right;
4423
    bool m_left_is_const;
4424
    bool m_right_is_const;
4425
    Mixed m_const_value;
4426
};
4427

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

4443
    // See comment in base class
4444
    void set_base_table(ConstTableRef table) override
4445
    {
74,274✔
4446
        m_left->set_base_table(table);
74,274✔
4447
        m_right->set_base_table(table);
74,274✔
4448
    }
74,274✔
4449

4450
    void set_cluster(const Cluster* cluster) override
4451
    {
121,713✔
4452
        if (m_has_matches) {
121,713✔
4453
            m_cluster = cluster;
2,286✔
4454
        }
2,286✔
4455
        else {
119,427✔
4456
            m_left->set_cluster(cluster);
119,427✔
4457
            m_right->set_cluster(cluster);
119,427✔
4458
        }
119,427✔
4459
    }
121,713✔
4460

4461
    // Recursively fetch tables of columns in expression tree. Used when user first builds a stand-alone expression
4462
    // and binds it to a Query at a later time
4463
    ConstTableRef get_base_table() const override
4464
    {
69,024✔
4465
        ConstTableRef l = m_left->get_base_table();
69,024✔
4466
        ConstTableRef r = m_right->get_base_table();
69,024✔
4467

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

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

4475
    void collect_dependencies(std::vector<TableKey>& tables) const override
4476
    {
9,234✔
4477
        m_left->collect_dependencies(tables);
9,234✔
4478
        m_right->collect_dependencies(tables);
9,234✔
4479
    }
9,234✔
4480

4481
    size_t find_first_with_matches(size_t start, size_t end) const
4482
    {
20,289✔
4483
        if (m_index_end == 0 || start >= end)
20,289✔
4484
            return not_found;
48✔
4485

4486
        ObjKey first_key = m_cluster->get_real_key(start);
20,241✔
4487
        ObjKey actual_key;
20,241✔
4488

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

4511
        // if actual key is bigger than last key, it is not in this leaf
4512
        ObjKey last_key = start + 1 == end ? first_key : m_cluster->get_real_key(end - 1);
17,031✔
4513
        if (actual_key > last_key)
17,031✔
4514
            return not_found;
1,269✔
4515

4516
        // key is known to be in this leaf, so find key whithin leaf keys
4517
        REALM_ASSERT(uint64_t(actual_key.value) >= m_cluster->get_offset());
15,762✔
4518
        return m_cluster->lower_bound_key(ClusterNode::RowKey(actual_key.value - m_cluster->get_offset()));
15,762✔
4519
    }
17,031✔
4520

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

4534
    std::unique_ptr<Subexpr> m_left;
4535
    std::unique_ptr<Subexpr> m_right;
4536
    const Cluster* m_cluster;
4537
    ValueBase* m_left_const_values = nullptr;
4538
    ValueBase* m_right_const_values = nullptr;
4539
    bool m_has_matches = false;
4540
    std::vector<ObjKey> m_matches;
4541
    mutable size_t m_index_get = 0;
4542
    size_t m_index_end = 0;
4543
};
4544

4545
template <class TCond>
4546
class Compare : public CompareBase {
4547
public:
4548
    using CompareBase::CompareBase;
4549

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

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

4600
                    m_has_matches = true;
1,914✔
4601
                    m_index_get = 0;
1,914✔
4602
                    m_index_end = m_matches.size();
1,914✔
4603
                    dT = 0;
1,914✔
4604
                }
1,914✔
4605
            }
37,722✔
4606
        }
61,366✔
4607

4608
        return dT;
43,029✔
4609
    }
79,612✔
4610

4611
    size_t find_first(size_t start, size_t end) const override
4612
    {
1,759,187✔
4613
        if (m_has_matches) {
1,759,187✔
4614
            return find_first_with_matches(start, end);
20,289✔
4615
        }
20,289✔
4616

4617
        size_t match;
1,738,898✔
4618
        ValueBase left_buf;
1,738,898✔
4619
        ValueBase right_buf;
1,738,898✔
4620
        const util::Optional<ExpressionComparisonType> left_cmp_type = m_left->get_comparison_type();
1,738,898✔
4621
        const util::Optional<ExpressionComparisonType> right_cmp_type = m_right->get_comparison_type();
1,738,898✔
4622

4623
        ValueBase* left = m_left_const_values ? m_left_const_values : &left_buf;
1,738,898✔
4624
        ValueBase* right = m_right_const_values ? m_right_const_values : &right_buf;
1,738,898✔
4625

4626
        for (; start < end;) {
4,078,553✔
4627
            // In case of wildcard query strings, we will get a value for every collection matching the path
4628
            // We need to match those separately against the other value - which might also come in multiple
4629
            // instances.
4630
            Subexpr::Index right_index(start);
3,990,593✔
4631
            do {
3,991,487✔
4632
                Subexpr::Index left_index(start);
3,991,487✔
4633
                if (!m_right_const_values) {
3,991,487✔
4634
                    m_right->evaluate(right_index, right_buf);
3,587,531✔
4635
                }
3,587,531✔
4636
                do {
3,993,371✔
4637
                    if (!m_left_const_values)
3,993,371✔
4638
                        m_left->evaluate(left_index, left_buf);
1,498,069✔
4639
                    match = ValueBase::template compare<TCond>(*left, *right, left_cmp_type, right_cmp_type);
3,993,371✔
4640
                    if (match != not_found && match + start < end)
3,993,371✔
4641
                        return start + match;
1,650,938✔
4642
                } while (left_index.more());
3,993,371✔
4643
            } while (right_index.more());
3,991,487✔
4644

4645
            size_t rows = (left->m_from_list || right->m_from_list) ? 1 : std::min(right->size(), left->size());
2,339,655✔
4646
            start += rows;
2,339,655✔
4647
        }
2,339,655✔
4648

4649
        return not_found; // no match
87,960✔
4650
    }
1,738,898✔
4651

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

4670
    std::unique_ptr<Expression> clone() const override
4671
    {
66,494✔
4672
        return std::unique_ptr<Expression>(new Compare(*this));
66,494✔
4673
    }
66,494✔
4674
};
4675
} // namespace realm
4676
#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