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

realm / realm-core / github_pull_request_275914

25 Sep 2023 03:10PM UTC coverage: 92.915% (+1.7%) from 91.215%
github_pull_request_275914

Pull #6073

Evergreen

jedelbo
Merge tag 'v13.21.0' into next-major

"Feature/Bugfix release"
Pull Request #6073: Merge next-major

96928 of 177706 branches covered (0.0%)

8324 of 8714 new or added lines in 122 files covered. (95.52%)

181 existing lines in 28 files now uncovered.

247505 of 266379 relevant lines covered (92.91%)

7164945.17 hits per line

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

90.74
/src/realm/parser/driver.cpp
1
/*************************************************************************
2
 *
3
 * Copyright 2021 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
#include "realm/parser/driver.hpp"
20
#include "realm/parser/keypath_mapping.hpp"
21
#include "realm/parser/query_parser.hpp"
22
#include "realm/sort_descriptor.hpp"
23
#include "realm/decimal128.hpp"
24
#include "realm/uuid.hpp"
25
#include "realm/util/base64.hpp"
26
#include "realm/util/overload.hpp"
27

28
#define YY_NO_UNISTD_H 1
29
#define YY_NO_INPUT 1
30
#include "realm/parser/generated/query_flex.hpp"
31

32
#include <external/mpark/variant.hpp>
33
#include <stdexcept>
34

35
using namespace realm;
36
using namespace std::string_literals;
37

38
// Whether to generate parser debug traces.
39
static bool trace_parsing = false;
40
// Whether to generate scanner debug traces.
41
static bool trace_scanning = false;
42

43
namespace {
44

45
const char* agg_op_type_to_str(query_parser::AggrNode::Type type)
46
{
256✔
47
    switch (type) {
256✔
48
        case realm::query_parser::AggrNode::MAX:
60✔
49
            return ".@max";
60✔
50
        case realm::query_parser::AggrNode::MIN:
68✔
51
            return ".@min";
68✔
52
        case realm::query_parser::AggrNode::SUM:
64✔
53
            return ".@sum";
64✔
54
        case realm::query_parser::AggrNode::AVG:
64✔
55
            return ".@avg";
64✔
56
    }
×
57
    return "";
×
58
}
×
59

60
const char* expression_cmp_type_to_str(util::Optional<ExpressionComparisonType> type)
61
{
44✔
62
    if (type) {
44✔
63
        switch (*type) {
44✔
64
            case ExpressionComparisonType::Any:
16✔
65
                return "ANY";
16✔
66
            case ExpressionComparisonType::All:
12✔
67
                return "ALL";
12✔
68
            case ExpressionComparisonType::None:
16✔
69
                return "NONE";
16✔
70
        }
×
71
    }
×
72
    return "";
×
73
}
×
74

75
std::string print_pretty_objlink(const ObjLink& link, const Group* g)
76
{
8✔
77
    REALM_ASSERT(g);
8✔
78
    if (link.is_null()) {
8✔
79
        return "NULL";
×
80
    }
×
81
    try {
8✔
82
        auto table = g->get_table(link.get_table_key());
8✔
83
        if (!table) {
8✔
84
            return "link to an invalid table";
×
85
        }
×
86
        auto obj = table->get_object(link.get_obj_key());
8✔
87
        Mixed pk = obj.get_primary_key();
8✔
88
        return util::format("'%1' with primary key '%2'", table->get_class_name(), util::serializer::print_value(pk));
8✔
89
    }
8✔
90
    catch (...) {
×
91
        return "invalid link";
×
92
    }
×
93
}
8✔
94

95
bool is_length_suffix(const std::string& s)
96
{
1,794✔
97
    return s.size() == 6 && (s[0] == 'l' || s[0] == 'L') && (s[1] == 'e' || s[1] == 'E') &&
1,794!
98
           (s[2] == 'n' || s[2] == 'N') && (s[3] == 'g' || s[3] == 'G') && (s[4] == 't' || s[4] == 'T') &&
1,786!
99
           (s[5] == 'h' || s[5] == 'H');
1,786!
100
}
1,794✔
101

102
template <typename T>
103
inline bool try_parse_specials(std::string str, T& ret)
104
{
8✔
105
    if constexpr (realm::is_any<T, float, double>::value || std::numeric_limits<T>::is_iec559) {
8✔
106
        std::transform(str.begin(), str.end(), str.begin(), toLowerAscii);
×
107
        if (std::numeric_limits<T>::has_quiet_NaN && (str == "nan" || str == "+nan")) {
×
108
            ret = std::numeric_limits<T>::quiet_NaN();
×
109
            return true;
×
110
        }
×
111
        else if (std::numeric_limits<T>::has_quiet_NaN && (str == "-nan")) {
×
112
            ret = -std::numeric_limits<T>::quiet_NaN();
×
113
            return true;
×
114
        }
×
115
        else if (std::numeric_limits<T>::has_infinity &&
×
116
                 (str == "+infinity" || str == "infinity" || str == "+inf" || str == "inf")) {
×
117
            ret = std::numeric_limits<T>::infinity();
×
118
            return true;
×
119
        }
×
120
        else if (std::numeric_limits<T>::has_infinity && (str == "-infinity" || str == "-inf")) {
×
121
            ret = -std::numeric_limits<T>::infinity();
×
122
            return true;
×
123
        }
×
124
    }
×
125
    return false;
×
126
}
×
127

128
template <typename T>
129
inline const char* get_type_name()
130
{
131
    return "unknown";
132
}
133
template <>
134
inline const char* get_type_name<int64_t>()
135
{
8✔
136
    return "number";
8✔
137
}
8✔
138
template <>
139
inline const char* get_type_name<float>()
140
{
×
141
    return "floating point number";
×
142
}
×
143
template <>
144
inline const char* get_type_name<double>()
145
{
×
146
    return "floating point number";
×
147
}
×
148

149
template <typename T>
150
inline T string_to(const std::string& s)
151
{
408✔
152
    std::istringstream iss(s);
408✔
153
    iss.imbue(std::locale::classic());
408✔
154
    T value;
408✔
155
    iss >> value;
408✔
156
    if (iss.fail()) {
408!
157
        if (!try_parse_specials(s, value)) {
8!
158
            throw InvalidQueryArgError(util::format("Cannot convert '%1' to a %2", s, get_type_name<T>()));
8✔
159
        }
8✔
160
    }
400✔
161
    return value;
400✔
162
}
400✔
163

164
class MixedArguments : public query_parser::Arguments {
165
public:
166
    using Arg = mpark::variant<Mixed, std::vector<Mixed>>;
167

168
    MixedArguments(const std::vector<Mixed>& args)
169
        : Arguments(args.size())
170
        , m_args([](const std::vector<Mixed>& args) -> std::vector<Arg> {
1,692✔
171
            std::vector<Arg> ret;
1,692✔
172
            ret.reserve(args.size());
1,692✔
173
            for (const Mixed& m : args) {
15,160✔
174
                ret.push_back(m);
15,160✔
175
            }
15,160✔
176
            return ret;
1,692✔
177
        }(args))
1,692✔
178
    {
1,692✔
179
    }
1,692✔
180
    MixedArguments(const std::vector<Arg>& args)
181
        : Arguments(args.size())
182
        , m_args(args)
183
    {
11,666✔
184
    }
11,666✔
185
    bool bool_for_argument(size_t n) final
186
    {
×
187
        return mixed_for_argument(n).get<bool>();
×
188
    }
×
189
    long long long_for_argument(size_t n) final
190
    {
364✔
191
        return mixed_for_argument(n).get<int64_t>();
364✔
192
    }
364✔
193
    float float_for_argument(size_t n) final
194
    {
×
195
        return mixed_for_argument(n).get<float>();
×
196
    }
×
197
    double double_for_argument(size_t n) final
198
    {
156✔
199
        return mixed_for_argument(n).get<double>();
156✔
200
    }
156✔
201
    StringData string_for_argument(size_t n) final
202
    {
36✔
203
        return mixed_for_argument(n).get<StringData>();
36✔
204
    }
36✔
205
    BinaryData binary_for_argument(size_t n) final
206
    {
×
207
        return mixed_for_argument(n).get<BinaryData>();
×
208
    }
×
209
    Timestamp timestamp_for_argument(size_t n) final
210
    {
×
211
        return mixed_for_argument(n).get<Timestamp>();
×
212
    }
×
213
    ObjectId objectid_for_argument(size_t n) final
214
    {
428,088✔
215
        return mixed_for_argument(n).get<ObjectId>();
428,088✔
216
    }
428,088✔
217
    UUID uuid_for_argument(size_t n) final
218
    {
12✔
219
        return mixed_for_argument(n).get<UUID>();
12✔
220
    }
12✔
221
    Decimal128 decimal128_for_argument(size_t n) final
222
    {
72✔
223
        return mixed_for_argument(n).get<Decimal128>();
72✔
224
    }
72✔
225
    ObjKey object_index_for_argument(size_t n) final
226
    {
×
227
        return mixed_for_argument(n).get<ObjKey>();
×
228
    }
×
229
    ObjLink objlink_for_argument(size_t n) final
230
    {
×
231
        return mixed_for_argument(n).get<ObjLink>();
×
232
    }
×
233
#if REALM_ENABLE_GEOSPATIAL
234
    Geospatial geospatial_for_argument(size_t n) final
235
    {
16✔
236
        return mixed_for_argument(n).get<Geospatial>();
16✔
237
    }
16✔
238
#endif
239
    std::vector<Mixed> list_for_argument(size_t n) final
240
    {
100✔
241
        Arguments::verify_ndx(n);
100✔
242
        return mpark::get<std::vector<Mixed>>(m_args[n]);
100✔
243
    }
100✔
244
    bool is_argument_null(size_t n) final
245
    {
428,924✔
246
        Arguments::verify_ndx(n);
428,924✔
247
        return visit(util::overload{
428,924✔
248
                         [](const Mixed& m) {
428,868✔
249
                             return m.is_null();
428,812✔
250
                         },
428,812✔
251
                         [](const std::vector<Mixed>&) {
214,512✔
252
                             return false;
100✔
253
                         },
100✔
254
                     },
428,924✔
255
                     m_args[n]);
428,924✔
256
    }
428,924✔
257
    bool is_argument_list(size_t n) final
258
    {
1,286,320✔
259
        Arguments::verify_ndx(n);
1,286,320✔
260
        static_assert(std::is_same_v<mpark::variant_alternative_t<1, Arg>, std::vector<Mixed>>);
1,286,320✔
261
        return m_args[n].index() == 1;
1,286,320✔
262
    }
1,286,320✔
263
    DataType type_for_argument(size_t n)
264
    {
428,760✔
265
        return mixed_for_argument(n).get_type();
428,760✔
266
    }
428,760✔
267

268
private:
269
    const Mixed& mixed_for_argument(size_t n)
270
    {
857,504✔
271
        Arguments::verify_ndx(n);
857,504✔
272
        if (is_argument_list(n)) {
857,504✔
273
            throw InvalidQueryArgError(
×
274
                util::format("Request for scalar argument at index %1 but a list was provided", n));
×
275
        }
×
276

428,752✔
277
        return mpark::get<Mixed>(m_args[n]);
857,504✔
278
    }
857,504✔
279

280
    const std::vector<Arg> m_args;
281
};
282

283
Timestamp get_timestamp_if_valid(int64_t seconds, int32_t nanoseconds)
284
{
548✔
285
    const bool both_non_negative = seconds >= 0 && nanoseconds >= 0;
548✔
286
    const bool both_non_positive = seconds <= 0 && nanoseconds <= 0;
548✔
287
    if (both_non_negative || both_non_positive) {
548✔
288
        return Timestamp(seconds, nanoseconds);
532✔
289
    }
532✔
290
    throw SyntaxError("Invalid timestamp format");
16✔
291
}
16✔
292

293
} // namespace
294

295
namespace realm {
296

297
namespace query_parser {
298

299
std::string_view string_for_op(CompareType op)
300
{
2,580✔
301
    switch (op) {
2,580✔
302
        case CompareType::EQUAL:
164✔
303
            return "=";
164✔
304
        case CompareType::NOT_EQUAL:
40✔
305
            return "!=";
40✔
306
        case CompareType::GREATER:
✔
307
            return ">";
×
308
        case CompareType::LESS:
✔
309
            return "<";
×
310
        case CompareType::GREATER_EQUAL:
✔
311
            return ">=";
×
312
        case CompareType::LESS_EQUAL:
✔
313
            return "<=";
×
314
        case CompareType::BEGINSWITH:
504✔
315
            return "beginswith";
504✔
316
        case CompareType::ENDSWITH:
500✔
317
            return "endswith";
500✔
318
        case CompareType::CONTAINS:
860✔
319
            return "contains";
860✔
320
        case CompareType::LIKE:
476✔
321
            return "like";
476✔
322
        case CompareType::IN:
8✔
323
            return "in";
8✔
324
        case CompareType::TEXT:
28✔
325
            return "text";
28✔
326
    }
×
327
    return ""; // appease MSVC warnings
×
328
}
×
329

330
NoArguments ParserDriver::s_default_args;
331
query_parser::KeyPathMapping ParserDriver::s_default_mapping;
332

333
ParserNode::~ParserNode() = default;
2,380,558✔
334

335
QueryNode::~QueryNode() = default;
904,292✔
336

337
Query NotNode::visit(ParserDriver* drv)
338
{
912✔
339
    Query q = drv->m_base_table->where();
912✔
340
    q.Not();
912✔
341
    q.and_query(query->visit(drv));
912✔
342
    return {q};
912✔
343
}
912✔
344

345
Query OrNode::visit(ParserDriver* drv)
346
{
556✔
347
    Query q(drv->m_base_table);
556✔
348
    q.group();
556✔
349
    for (auto it : children) {
430,458✔
350
        q.Or();
430,458✔
351
        q.and_query(it->visit(drv));
430,458✔
352
    }
430,458✔
353
    q.end_group();
556✔
354

278✔
355
    return q;
556✔
356
}
556✔
357

358
Query AndNode::visit(ParserDriver* drv)
359
{
708✔
360
    Query q(drv->m_base_table);
708✔
361
    for (auto it : children) {
1,592✔
362
        q.and_query(it->visit(drv));
1,592✔
363
    }
1,592✔
364
    return q;
708✔
365
}
708✔
366

367
static void verify_only_string_types(DataType type, std::string_view op_string)
368
{
2,580✔
369
    if (type != type_String && type != type_Binary && type != type_Mixed) {
2,580✔
370
        throw InvalidQueryError(util::format(
348✔
371
            "Unsupported comparison operator '%1' against type '%2', right side must be a string or binary type",
348✔
372
            op_string, get_data_type_name(type)));
348✔
373
    }
348✔
374
}
2,580✔
375

376
std::unique_ptr<Subexpr> OperationNode::visit(ParserDriver* drv, DataType type)
377
{
1,056✔
378
    std::unique_ptr<Subexpr> left;
1,056✔
379
    std::unique_ptr<Subexpr> right;
1,056✔
380

528✔
381
    const bool left_is_constant = m_left->is_constant();
1,056✔
382
    const bool right_is_constant = m_right->is_constant();
1,056✔
383
    const bool produces_multiple_values = m_left->is_list() || m_right->is_list();
1,056✔
384

528✔
385
    if (left_is_constant && right_is_constant && !produces_multiple_values) {
1,056✔
386
        right = m_right->visit(drv, type);
48✔
387
        left = m_left->visit(drv, type);
48✔
388
        auto v_left = left->get_mixed();
48✔
389
        auto v_right = right->get_mixed();
48✔
390
        Mixed result;
48✔
391
        switch (m_op) {
48✔
392
            case '+':
28✔
393
                result = v_left + v_right;
28✔
394
                break;
28✔
395
            case '-':
✔
396
                result = v_left - v_right;
×
397
                break;
×
398
            case '*':
20✔
399
                result = v_left * v_right;
20✔
400
                break;
20✔
401
            case '/':
✔
402
                result = v_left / v_right;
×
403
                break;
×
404
            default:
✔
405
                break;
×
406
        }
48✔
407
        return std::make_unique<Value<Mixed>>(result);
48✔
408
    }
48✔
409

504✔
410
    if (right_is_constant) {
1,008✔
411
        // Take left first - it cannot be a constant
208✔
412
        left = m_left->visit(drv);
416✔
413

208✔
414
        right = m_right->visit(drv, left->get_type());
416✔
415
    }
416✔
416
    else {
592✔
417
        right = m_right->visit(drv);
592✔
418
        if (left_is_constant) {
592✔
419
            left = m_left->visit(drv, right->get_type());
152✔
420
        }
152✔
421
        else {
440✔
422
            left = m_left->visit(drv);
440✔
423
        }
440✔
424
    }
592✔
425
    if (!Mixed::is_numeric(left->get_type(), right->get_type())) {
1,008✔
426
        util::serializer::SerialisationState state;
16✔
427
        std::string op(&m_op, 1);
16✔
428
        throw InvalidQueryArgError(util::format("Cannot perform '%1' operation on '%2' and '%3'", op,
16✔
429
                                                left->description(state), right->description(state)));
16✔
430
    }
16✔
431

496✔
432
    switch (m_op) {
992✔
433
        case '+':
400✔
434
            return std::make_unique<Operator<Plus>>(std::move(left), std::move(right));
400✔
435
        case '-':
136✔
436
            return std::make_unique<Operator<Minus>>(std::move(left), std::move(right));
136✔
437
        case '*':
260✔
438
            return std::make_unique<Operator<Mul>>(std::move(left), std::move(right));
260✔
439
        case '/':
196✔
440
            return std::make_unique<Operator<Div>>(std::move(left), std::move(right));
196✔
441
        default:
✔
442
            break;
×
443
    }
×
444
    return {};
×
445
}
×
446

447
Query EqualityNode::visit(ParserDriver* drv)
448
{
463,068✔
449
    auto [left, right] = drv->cmp(values);
463,068✔
450

231,050✔
451
    auto left_type = left->get_type();
463,068✔
452
    auto right_type = right->get_type();
463,068✔
453

231,050✔
454
    auto handle_typed_links = [drv](std::unique_ptr<Subexpr>& list, std::unique_ptr<Subexpr>& expr, DataType& type) {
231,240✔
455
        if (auto link_column = dynamic_cast<const Columns<Link>*>(list.get())) {
380✔
456
            // Change all TypedLink values to ObjKey values
190✔
457
            auto value = dynamic_cast<ValueBase*>(expr.get());
380✔
458
            auto left_dest_table_key = link_column->link_map().get_target_table()->get_key();
380✔
459
            auto sz = value->size();
380✔
460
            auto obj_keys = std::make_unique<Value<ObjKey>>();
380✔
461
            obj_keys->init(expr->has_multiple_values(), sz);
380✔
462
            obj_keys->set_comparison_type(expr->get_comparison_type());
380✔
463
            for (size_t i = 0; i < sz; i++) {
764✔
464
                auto val = value->get(i);
396✔
465
                // i'th entry is already NULL
198✔
466
                if (!val.is_null()) {
396✔
467
                    TableKey right_table_key;
196✔
468
                    ObjKey right_obj_key;
196✔
469
                    if (val.is_type(type_Link)) {
196✔
470
                        right_table_key = left_dest_table_key;
20✔
471
                        right_obj_key = val.get<ObjKey>();
20✔
472
                    }
20✔
473
                    else if (val.is_type(type_TypedLink)) {
176✔
474
                        right_table_key = val.get_link().get_table_key();
172✔
475
                        right_obj_key = val.get_link().get_obj_key();
172✔
476
                    }
172✔
477
                    else {
4✔
478
                        const char* target_type = get_data_type_name(val.get_type());
4✔
479
                        throw InvalidQueryError(
4✔
480
                            util::format("Unsupported comparison between '%1' and type '%2'",
4✔
481
                                         link_column->link_map().description(drv->m_serializer_state), target_type));
4✔
482
                    }
4✔
483
                    if (left_dest_table_key == right_table_key) {
192✔
484
                        obj_keys->set(i, right_obj_key);
184✔
485
                    }
184✔
486
                    else {
8✔
487
                        const Group* g = drv->m_base_table->get_parent_group();
8✔
488
                        throw InvalidQueryArgError(
8✔
489
                            util::format("The relationship '%1' which links to type '%2' cannot be compared to "
8✔
490
                                         "an argument of type %3",
8✔
491
                                         link_column->link_map().description(drv->m_serializer_state),
8✔
492
                                         link_column->link_map().get_target_table()->get_class_name(),
8✔
493
                                         print_pretty_objlink(ObjLink(right_table_key, right_obj_key), g)));
8✔
494
                    }
8✔
495
                }
192✔
496
            }
396✔
497
            expr = std::move(obj_keys);
374✔
498
            type = type_Link;
368✔
499
        }
368✔
500
    };
380✔
501

231,050✔
502
    if (left_type == type_Link && right->has_constant_evaluation()) {
463,068✔
503
        handle_typed_links(left, right, right_type);
368✔
504
    }
368✔
505
    if (right_type == type_Link && left->has_constant_evaluation()) {
463,068✔
506
        handle_typed_links(right, left, left_type);
12✔
507
    }
12✔
508

231,050✔
509
    if (left_type.is_valid() && right_type.is_valid() && !Mixed::data_types_are_comparable(left_type, right_type)) {
463,068✔
510
        throw InvalidQueryError(util::format("Unsupported comparison between type '%1' and type '%2'",
148✔
511
                                             get_data_type_name(left_type), get_data_type_name(right_type)));
148✔
512
    }
148✔
513
    if (left_type == type_TypeOfValue || right_type == type_TypeOfValue) {
462,920✔
514
        if (left_type != right_type) {
512✔
515
            throw InvalidQueryArgError(
12✔
516
                util::format("Unsupported comparison between @type and raw value: '%1' and '%2'",
12✔
517
                             get_data_type_name(left_type), get_data_type_name(right_type)));
12✔
518
        }
12✔
519
    }
462,908✔
520

230,972✔
521
    if (op == CompareType::IN) {
462,908✔
522
        Subexpr* r = right.get();
656✔
523
        if (!r->has_multiple_values()) {
656✔
524
            throw InvalidQueryArgError("The keypath following 'IN' must contain a list. Found '" +
8✔
525
                                       r->description(drv->m_serializer_state) + "'");
8✔
526
        }
8✔
527
    }
462,900✔
528

230,968✔
529
    if (left_type == type_Link && left_type == right_type && right->has_constant_evaluation()) {
462,900✔
530
        if (auto link_column = dynamic_cast<const Columns<Link>*>(left.get())) {
356✔
531
            if (link_column->link_map().get_nb_hops() == 1 &&
356✔
532
                link_column->get_comparison_type().value_or(ExpressionComparisonType::Any) ==
328✔
533
                    ExpressionComparisonType::Any) {
292✔
534
                REALM_ASSERT(dynamic_cast<const Value<ObjKey>*>(right.get()));
284✔
535
                auto link_values = static_cast<const Value<ObjKey>*>(right.get());
284✔
536
                // We can use a LinksToNode based query
142✔
537
                std::vector<ObjKey> values;
284✔
538
                values.reserve(link_values->size());
284✔
539
                for (auto val : *link_values) {
300✔
540
                    values.emplace_back(val.is_null() ? ObjKey() : val.get<ObjKey>());
234✔
541
                }
300✔
542
                if (op == CompareType::EQUAL) {
284✔
543
                    return drv->m_base_table->where().links_to(link_column->link_map().get_first_column_key(),
184✔
544
                                                               values);
184✔
545
                }
184✔
546
                else if (op == CompareType::NOT_EQUAL) {
100✔
547
                    return drv->m_base_table->where().not_links_to(link_column->link_map().get_first_column_key(),
96✔
548
                                                                   values);
96✔
549
                }
96✔
550
            }
462,544✔
551
        }
356✔
552
    }
462,544✔
553
    else if (right->has_single_value() && (left_type == right_type || left_type == type_Mixed)) {
462,544✔
554
        Mixed val = right->get_mixed();
456,026✔
555
        const ObjPropertyBase* prop = dynamic_cast<const ObjPropertyBase*>(left.get());
456,026✔
556
        if (prop && !prop->links_exist()) {
456,026✔
557
            auto col_key = prop->column_key();
443,190✔
558
            if (val.is_null()) {
443,190✔
559
                switch (op) {
188✔
560
                    case CompareType::EQUAL:
132✔
561
                    case CompareType::IN:
132✔
562
                        return drv->m_base_table->where().equal(col_key, realm::null());
132✔
563
                    case CompareType::NOT_EQUAL:
94✔
564
                        return drv->m_base_table->where().not_equal(col_key, realm::null());
56✔
565
                    default:
66✔
566
                        break;
×
567
                }
443,002✔
568
            }
443,002✔
569
            switch (left->get_type()) {
443,002✔
570
                case type_Int:
9,780✔
571
                    return drv->simple_query(op, col_key, val.get_int());
9,780✔
572
                case type_Bool:
132✔
573
                    return drv->simple_query(op, col_key, val.get_bool());
132✔
574
                case type_String:
2,394✔
575
                    return drv->simple_query(op, col_key, val.get_string(), case_sensitive);
2,394✔
576
                case type_Binary:
1,068✔
577
                    return drv->simple_query(op, col_key, val.get_binary(), case_sensitive);
1,068✔
578
                case type_Timestamp:
140✔
579
                    return drv->simple_query(op, col_key, val.get<Timestamp>());
140✔
580
                case type_Float:
52✔
581
                    return drv->simple_query(op, col_key, val.get_float());
52✔
582
                case type_Double:
100✔
583
                    return drv->simple_query(op, col_key, val.get_double());
100✔
584
                case type_Decimal:
696✔
585
                    return drv->simple_query(op, col_key, val.get<Decimal128>());
696✔
586
                case type_ObjectId:
428,384✔
587
                    return drv->simple_query(op, col_key, val.get<ObjectId>());
428,384✔
588
                case type_UUID:
164✔
589
                    return drv->simple_query(op, col_key, val.get<UUID>());
164✔
590
                case type_Mixed:
92✔
591
                    return drv->simple_query(op, col_key, val, case_sensitive);
92✔
592
                default:
✔
593
                    break;
×
594
            }
19,430✔
595
        }
19,430✔
596
    }
456,026✔
597
    if (case_sensitive) {
19,430✔
598
        switch (op) {
18,806✔
599
            case CompareType::EQUAL:
15,910✔
600
            case CompareType::IN:
16,230✔
601
                return Query(std::unique_ptr<Expression>(new Compare<Equal>(std::move(left), std::move(right))));
16,230✔
602
            case CompareType::NOT_EQUAL:
9,114✔
603
                return Query(std::unique_ptr<Expression>(new Compare<NotEqual>(std::move(left), std::move(right))));
2,576✔
604
            default:
7,826✔
605
                break;
×
606
        }
624✔
607
    }
624✔
608
    else {
624✔
609
        verify_only_string_types(right_type, util::format("%1%2", string_for_op(op), "[c]"));
624✔
610
        switch (op) {
624✔
611
            case CompareType::EQUAL:
108✔
612
            case CompareType::IN:
112✔
613
                return Query(std::unique_ptr<Expression>(new Compare<EqualIns>(std::move(left), std::move(right))));
112✔
614
            case CompareType::NOT_EQUAL:
76✔
615
                return Query(
40✔
616
                    std::unique_ptr<Expression>(new Compare<NotEqualIns>(std::move(left), std::move(right))));
40✔
617
            default:
56✔
618
                break;
×
619
        }
×
620
    }
×
621
    return {};
×
622
}
×
623

624
Query BetweenNode::visit(ParserDriver* drv)
625
{
92✔
626
    if (limits->elements.size() != 2) {
92✔
627
        throw InvalidQueryError("Operator 'BETWEEN' requires list with 2 elements.");
8✔
628
    }
8✔
629

42✔
630
    if (dynamic_cast<ColumnListBase*>(prop->visit(drv, type_Int).get())) {
84✔
631
        // It's a list!
8✔
632
        util::Optional<ExpressionComparisonType> cmp_type = dynamic_cast<PropertyNode*>(prop)->comp_type;
16✔
633
        if (cmp_type.value_or(ExpressionComparisonType::Any) != ExpressionComparisonType::All) {
16✔
634
            throw InvalidQueryError("Only 'ALL' supported for operator 'BETWEEN' when applied to lists.");
12✔
635
        }
12✔
636
    }
72✔
637

36✔
638
    auto& min(limits->elements.at(0));
72✔
639
    auto& max(limits->elements.at(1));
72✔
640
    RelationalNode cmp1(prop, CompareType::GREATER_EQUAL, min);
72✔
641
    RelationalNode cmp2(prop, CompareType::LESS_EQUAL, max);
72✔
642

36✔
643
    Query q(drv->m_base_table);
72✔
644
    q.and_query(cmp1.visit(drv));
72✔
645
    q.and_query(cmp2.visit(drv));
72✔
646

36✔
647
    return q;
72✔
648
}
72✔
649

650
Query RelationalNode::visit(ParserDriver* drv)
651
{
5,136✔
652
    auto [left, right] = drv->cmp(values);
5,136✔
653

2,558✔
654
    auto left_type = left->get_type();
5,136✔
655
    auto right_type = right->get_type();
5,136✔
656
    const bool right_type_is_null = right->has_single_value() && right->get_mixed().is_null();
5,136✔
657
    const bool left_type_is_null = left->has_single_value() && left->get_mixed().is_null();
5,136✔
658
    REALM_ASSERT(!(left_type_is_null && right_type_is_null));
5,136!
659

2,558✔
660
    if (left_type == type_Link || left_type == type_TypeOfValue) {
5,136✔
661
        throw InvalidQueryError(util::format(
×
662
            "Unsupported operator %1 in query. Only equal (==) and not equal (!=) are supported for this type.",
×
663
            string_for_op(op)));
×
664
    }
×
665

2,558✔
666
    if (!(left_type_is_null || right_type_is_null) && (!left_type.is_valid() || !right_type.is_valid() ||
5,136✔
667
                                                       !Mixed::data_types_are_comparable(left_type, right_type))) {
4,644✔
668
        throw InvalidQueryError(util::format("Unsupported comparison between type '%1' and type '%2'",
36✔
669
                                             get_data_type_name(left_type), get_data_type_name(right_type)));
36✔
670
    }
36✔
671

2,540✔
672
    const ObjPropertyBase* prop = dynamic_cast<const ObjPropertyBase*>(left.get());
5,100✔
673
    if (prop && !prop->links_exist() && right->has_single_value() &&
5,100✔
674
        (left_type == right_type || left_type == type_Mixed)) {
3,416✔
675
        auto col_key = prop->column_key();
1,464✔
676
        switch (left->get_type()) {
1,464✔
677
            case type_Int:
692✔
678
                return drv->simple_query(op, col_key, right->get_mixed().get_int());
692✔
679
            case type_Bool:
✔
680
                break;
×
681
            case type_String:
✔
682
                break;
×
683
            case type_Binary:
✔
684
                break;
×
685
            case type_Timestamp:
48✔
686
                return drv->simple_query(op, col_key, right->get_mixed().get<Timestamp>());
48✔
687
            case type_Float:
68✔
688
                return drv->simple_query(op, col_key, right->get_mixed().get_float());
68✔
689
                break;
×
690
            case type_Double:
144✔
691
                return drv->simple_query(op, col_key, right->get_mixed().get_double());
144✔
692
                break;
×
693
            case type_Decimal:
16✔
694
                return drv->simple_query(op, col_key, right->get_mixed().get<Decimal128>());
16✔
695
                break;
×
696
            case type_ObjectId:
288✔
697
                return drv->simple_query(op, col_key, right->get_mixed().get<ObjectId>());
288✔
698
                break;
×
699
            case type_UUID:
128✔
700
                return drv->simple_query(op, col_key, right->get_mixed().get<UUID>());
128✔
701
                break;
×
702
            case type_Mixed:
80✔
703
                return drv->simple_query(op, col_key, right->get_mixed());
80✔
704
                break;
×
705
            default:
✔
706
                break;
×
707
        }
3,636✔
708
    }
3,636✔
709
    switch (op) {
3,636✔
710
        case CompareType::GREATER:
2,240✔
711
            return Query(std::unique_ptr<Expression>(new Compare<Greater>(std::move(left), std::move(right))));
2,240✔
712
        case CompareType::LESS:
284✔
713
            return Query(std::unique_ptr<Expression>(new Compare<Less>(std::move(left), std::move(right))));
284✔
714
        case CompareType::GREATER_EQUAL:
540✔
715
            return Query(std::unique_ptr<Expression>(new Compare<GreaterEqual>(std::move(left), std::move(right))));
540✔
716
        case CompareType::LESS_EQUAL:
172✔
717
            return Query(std::unique_ptr<Expression>(new Compare<LessEqual>(std::move(left), std::move(right))));
172✔
718
        default:
✔
719
            break;
×
720
    }
×
721
    return {};
×
722
}
×
723

724
Query StringOpsNode::visit(ParserDriver* drv)
725
{
2,384✔
726
    auto [left, right] = drv->cmp(values);
2,384✔
727

1,174✔
728
    auto left_type = left->get_type();
2,384✔
729
    auto right_type = right->get_type();
2,384✔
730
    const ObjPropertyBase* prop = dynamic_cast<const ObjPropertyBase*>(left.get());
2,384✔
731

1,174✔
732
    verify_only_string_types(right_type, string_for_op(op));
2,384✔
733

1,174✔
734
    if (prop && !prop->links_exist() && right->has_single_value() &&
2,384✔
735
        (left_type == right_type || left_type == type_Mixed)) {
1,442✔
736
        auto col_key = prop->column_key();
464✔
737
        if (right_type == type_String) {
464✔
738
            StringData val = right->get_mixed().get_string();
316✔
739

156✔
740
            switch (op) {
316✔
741
                case CompareType::BEGINSWITH:
54✔
742
                    return drv->m_base_table->where().begins_with(col_key, val, case_sensitive);
54✔
743
                case CompareType::ENDSWITH:
62✔
744
                    return drv->m_base_table->where().ends_with(col_key, val, case_sensitive);
62✔
745
                case CompareType::CONTAINS:
120✔
746
                    return drv->m_base_table->where().contains(col_key, val, case_sensitive);
120✔
747
                case CompareType::LIKE:
56✔
748
                    return drv->m_base_table->where().like(col_key, val, case_sensitive);
56✔
749
                case CompareType::TEXT:
24✔
750
                    return drv->m_base_table->where().fulltext(col_key, val);
24✔
751
                case CompareType::IN:
✔
752
                case CompareType::EQUAL:
✔
753
                case CompareType::NOT_EQUAL:
✔
754
                case CompareType::GREATER:
✔
755
                case CompareType::LESS:
✔
756
                case CompareType::GREATER_EQUAL:
✔
757
                case CompareType::LESS_EQUAL:
✔
758
                    break;
×
759
            }
148✔
760
        }
148✔
761
        else if (right_type == type_Binary) {
148✔
762
            BinaryData val = right->get_mixed().get_binary();
148✔
763

40✔
764
            switch (op) {
148✔
765
                case CompareType::BEGINSWITH:
34✔
766
                    return drv->m_base_table->where().begins_with(col_key, val, case_sensitive);
34✔
767
                case CompareType::ENDSWITH:
38✔
768
                    return drv->m_base_table->where().ends_with(col_key, val, case_sensitive);
38✔
769
                case CompareType::CONTAINS:
48✔
770
                    return drv->m_base_table->where().contains(col_key, val, case_sensitive);
48✔
771
                case CompareType::LIKE:
28✔
772
                    return drv->m_base_table->where().like(col_key, val, case_sensitive);
28✔
773
                case CompareType::TEXT:
✔
774
                case CompareType::IN:
✔
775
                case CompareType::EQUAL:
✔
776
                case CompareType::NOT_EQUAL:
✔
777
                case CompareType::GREATER:
✔
778
                case CompareType::LESS:
✔
779
                case CompareType::GREATER_EQUAL:
✔
780
                case CompareType::LESS_EQUAL:
✔
781
                    break;
×
782
            }
1,920✔
783
        }
1,920✔
784
    }
464✔
785

978✔
786
    if (case_sensitive) {
1,920✔
787
        switch (op) {
752✔
788
            case CompareType::BEGINSWITH:
148✔
789
                return Query(std::unique_ptr<Expression>(new Compare<BeginsWith>(std::move(right), std::move(left))));
148✔
790
            case CompareType::ENDSWITH:
156✔
791
                return Query(std::unique_ptr<Expression>(new Compare<EndsWith>(std::move(right), std::move(left))));
156✔
792
            case CompareType::CONTAINS:
288✔
793
                return Query(std::unique_ptr<Expression>(new Compare<Contains>(std::move(right), std::move(left))));
288✔
794
            case CompareType::LIKE:
156✔
795
                return Query(std::unique_ptr<Expression>(new Compare<Like>(std::move(right), std::move(left))));
156✔
796
            case CompareType::TEXT: {
4✔
797
                StringData val = right->get_mixed().get_string();
4✔
798
                auto string_prop = dynamic_cast<Columns<StringData>*>(left.get());
4✔
799
                return string_prop->fulltext(val);
4✔
800
            }
×
801
            case CompareType::IN:
✔
802
            case CompareType::EQUAL:
✔
803
            case CompareType::NOT_EQUAL:
✔
804
            case CompareType::GREATER:
✔
805
            case CompareType::LESS:
✔
806
            case CompareType::GREATER_EQUAL:
✔
807
            case CompareType::LESS_EQUAL:
✔
808
                break;
×
809
        }
1,168✔
810
    }
1,168✔
811
    else {
1,168✔
812
        switch (op) {
1,168✔
813
            case CompareType::BEGINSWITH:
196✔
814
                return Query(
196✔
815
                    std::unique_ptr<Expression>(new Compare<BeginsWithIns>(std::move(right), std::move(left))));
196✔
816
            case CompareType::ENDSWITH:
172✔
817
                return Query(
172✔
818
                    std::unique_ptr<Expression>(new Compare<EndsWithIns>(std::move(right), std::move(left))));
172✔
819
            case CompareType::CONTAINS:
332✔
820
                return Query(
332✔
821
                    std::unique_ptr<Expression>(new Compare<ContainsIns>(std::move(right), std::move(left))));
332✔
822
            case CompareType::LIKE:
164✔
823
                return Query(std::unique_ptr<Expression>(new Compare<LikeIns>(std::move(right), std::move(left))));
164✔
824
            case CompareType::IN:
✔
825
            case CompareType::EQUAL:
✔
826
            case CompareType::NOT_EQUAL:
✔
827
            case CompareType::GREATER:
✔
828
            case CompareType::LESS:
✔
829
            case CompareType::GREATER_EQUAL:
✔
830
            case CompareType::LESS_EQUAL:
✔
831
            case CompareType::TEXT:
✔
832
                break;
×
833
        }
×
834
    }
×
835
    return {};
×
836
}
×
837

838
#if REALM_ENABLE_GEOSPATIAL
839
Query GeoWithinNode::visit(ParserDriver* drv)
840
{
176✔
841
    auto left = prop->visit(drv);
176✔
842
    auto left_type = left->get_type();
176✔
843
    if (left_type != type_Link) {
176✔
844
        throw InvalidQueryError(util::format("The left hand side of 'geoWithin' must be a link to geoJSON formatted "
4✔
845
                                             "data. But the provided type is '%1'",
4✔
846
                                             get_data_type_name(left_type)));
4✔
847
    }
4✔
848
    auto link_column = dynamic_cast<const Columns<Link>*>(left.get());
172✔
849

86✔
850
    if (geo) {
172✔
851
        auto right = geo->visit(drv, type_Int);
124✔
852
        auto geo_value = dynamic_cast<const ConstantGeospatialValue*>(right.get());
124✔
853
        return link_column->geo_within(geo_value->get_mixed().get<Geospatial>());
124✔
854
    }
124✔
855

24✔
856
    REALM_ASSERT_3(argument.size(), >, 1);
48✔
857
    REALM_ASSERT_3(argument[0], ==, '$');
48✔
858
    size_t arg_no = size_t(strtol(argument.substr(1).c_str(), nullptr, 10));
48✔
859
    auto right_type = drv->m_args.is_argument_null(arg_no) ? DataType(-1) : drv->m_args.type_for_argument(arg_no);
46✔
860

24✔
861
    Geospatial geo_from_argument;
48✔
862
    if (right_type == type_Geospatial) {
48✔
863
        geo_from_argument = drv->m_args.geospatial_for_argument(arg_no);
16✔
864
    }
16✔
865
    else if (right_type == type_String) {
32✔
866
        // This is a "hack" to allow users to pass in geospatial objects
10✔
867
        // serialized as a string instead of as a native type. This is because
10✔
868
        // the CAPI doesn't have support for marshalling polygons (of variable length)
10✔
869
        // yet and that project was deprioritized to geospatial phase 2. This should be
10✔
870
        // removed once SDKs are all using the binding generator.
10✔
871
        std::string str_val = drv->m_args.string_for_argument(arg_no);
20✔
872
        const std::string simulated_prefix = "simulated GEOWITHIN ";
20✔
873
        str_val = simulated_prefix + str_val;
20✔
874
        ParserDriver sub_driver;
20✔
875
        try {
20✔
876
            sub_driver.parse(str_val);
20✔
877
        }
20✔
878
        catch (const std::exception& ex) {
14✔
879
            std::string doctored_err = ex.what();
8✔
880
            size_t prefix_location = doctored_err.find(simulated_prefix);
8✔
881
            if (prefix_location != std::string::npos) {
8✔
882
                doctored_err.erase(prefix_location, simulated_prefix.size());
8✔
883
            }
8✔
884
            throw InvalidQueryError(util::format(
8✔
885
                "Invalid syntax in serialized geospatial object at argument %1: '%2'", arg_no, doctored_err));
8✔
886
        }
8✔
887
        GeoWithinNode* node = dynamic_cast<GeoWithinNode*>(sub_driver.result);
12✔
888
        REALM_ASSERT(node);
12✔
889
        if (node->geo) {
12✔
890
            if (node->geo->m_geo.get_type() != Geospatial::Type::Invalid) {
12✔
891
                geo_from_argument = node->geo->m_geo;
4✔
892
            }
4✔
893
            else {
8✔
894
                geo_from_argument = GeoPolygon{node->geo->m_points};
8✔
895
            }
8✔
896
        }
12✔
897
    }
12✔
898
    else {
12✔
899
        throw InvalidQueryError(util::format("The right hand side of 'geoWithin' must be a geospatial constant "
12✔
900
                                             "value. But the provided type is '%1'",
12✔
901
                                             get_data_type_name(right_type)));
12✔
902
    }
12✔
903

14✔
904
    if (geo_from_argument.get_type() == Geospatial::Type::Invalid) {
28✔
905
        throw InvalidQueryError(util::format(
4✔
906
            "The right hand side of 'geoWithin' must be a valid Geospatial value, got '%1'", geo_from_argument));
4✔
907
    }
4✔
908
    Status geo_status = geo_from_argument.is_valid();
24✔
909
    if (!geo_status.is_ok()) {
24✔
910
        throw InvalidQueryError(
×
911
            util::format("The Geospatial query argument region is invalid: '%1'", geo_status.reason()));
×
912
    }
×
913
    return link_column->geo_within(geo_from_argument);
24✔
914
}
24✔
915
#endif
916

917
Query TrueOrFalseNode::visit(ParserDriver* drv)
918
{
612✔
919
    Query q = drv->m_base_table->where();
612✔
920
    if (true_or_false) {
612✔
921
        q.and_query(std::unique_ptr<realm::Expression>(new TrueExpression));
460✔
922
    }
460✔
923
    else {
152✔
924
        q.and_query(std::unique_ptr<realm::Expression>(new FalseExpression));
152✔
925
    }
152✔
926
    return q;
612✔
927
}
612✔
928

929
std::unique_ptr<Subexpr> PropertyNode::visit(ParserDriver* drv, DataType)
930
{
473,468✔
931
    bool is_keys = false;
473,468✔
932
    std::string identifier = path->path_elems.back().id;
236,374✔
933
    if (identifier[0] == '@') {
236,222✔
934
        if (identifier == "@values") {
428✔
935
            path->path_elems.pop_back();
162✔
936
            identifier = path->path_elems.back().id;
162✔
937
        }
162✔
938
        else if (identifier == "@keys") {
418✔
939
            path->path_elems.pop_back();
237,116✔
940
            identifier = path->path_elems.back().id;
237,116✔
941
            is_keys = true;
236,258✔
942
        }
24✔
943
        else if (identifier == "@links") {
246✔
944
            // This is a backlink aggregate query
236,386✔
945
            auto link_chain = path->visit(drv, comp_type);
236,386✔
946
            auto sub = link_chain.get_backlink_count<Int>();
237,244✔
947
            return sub.clone();
152✔
948
        }
237,244✔
949
    }
475,034✔
950
    try {
237,942✔
951
        m_link_chain = path->visit(drv, comp_type);
237,942✔
952
        std::unique_ptr<Subexpr> subexpr;
236,070✔
953
        if (is_keys || !path->path_elems.back().index.is_null()) {
473,162✔
954
            // It must be a dictionary
1,802✔
955
            subexpr = drv->dictionary_column(m_link_chain, identifier);
1,802✔
956
            auto s = dynamic_cast<Columns<Dictionary>*>(subexpr.get());
1,802✔
957
            if (!s) {
62✔
UNCOV
958
                throw InvalidQueryError("Not a dictionary");
×
959
            }
1,740✔
960
            if (is_keys) {
126✔
961
                subexpr = std::make_unique<ColumnDictionaryKeys>(*s);
86✔
962
            }
86✔
963
            else {
1,716✔
964
                subexpr = s->key(path->path_elems.back().index).clone();
152✔
965
            }
130✔
966
        }
152✔
967
        else {
236,098✔
968
            subexpr = drv->column(m_link_chain, identifier);
236,010✔
969
        }
236,096✔
970

236,092✔
971
        if (post_op) {
236,092✔
972
            return post_op->visit(drv, subexpr.get());
2,064✔
973
        }
2,064✔
974
        return subexpr;
234,138✔
975
    }
234,162✔
976
    catch (const InvalidQueryError&) {
972✔
977
        // Is 'identifier' perhaps length operator?
968✔
978
        if (!post_op && is_length_suffix(identifier) && path->path_elems.size() > 1) {
968✔
979
            // If 'length' is the operator, the last id in the path must be the name
894✔
980
            // of a list property
988✔
981
            path->path_elems.pop_back();
2,440✔
982
            std::string& prop = path->path_elems.back().id;
896✔
983
            std::unique_ptr<Subexpr> subexpr{path->visit(drv, comp_type).column(prop)};
896✔
984
            if (auto list = dynamic_cast<ColumnListBase*>(subexpr.get())) {
2,420✔
985
                if (auto length_expr = list->get_element_length())
2,384✔
986
                    return length_expr;
2,352✔
987
            }
1,678✔
988
        }
2,416✔
989
        throw;
138✔
990
    }
1,878✔
991
    REALM_UNREACHABLE();
872✔
992
    return {};
852✔
993
}
852✔
994

995
std::unique_ptr<Subexpr> SubqueryNode::visit(ParserDriver* drv, DataType)
996
{
996✔
997
    if (variable_name.size() < 2 || variable_name[0] != '$') {
996✔
998
        throw SyntaxError(util::format("The subquery variable '%1' is invalid. The variable must start with "
846✔
999
                                       "'$' and cannot be empty; for example '$x'.",
846✔
1000
                                       variable_name));
846✔
1001
    }
814✔
1002
    LinkChain lc = prop->path->visit(drv, prop->comp_type);
190✔
1003
    std::string identifier = prop->path->path_elems.back().id;
994✔
1004
    identifier = drv->translate(lc, identifier);
190✔
1005

190✔
1006
    if (identifier.find("@links") == 0) {
190✔
1007
        drv->backlink(lc, identifier);
50✔
1008
    }
30✔
1009
    else {
160✔
1010
        ColKey col_key = lc.get_current_table()->get_column_key(identifier);
160✔
1011
        if (col_key.is_list() && col_key.get_type() != col_type_LinkList) {
160✔
1012
            throw InvalidQueryError(
22✔
1013
                util::format("A subquery can not operate on a list of primitive values (property '%1')", identifier));
236,222✔
1014
        }
1,742✔
1015
        if (col_key.get_type() != col_type_LinkList) {
236,358✔
1016
            throw InvalidQueryError(util::format("A subquery must operate on a list property, but '%1' is type '%2'",
2,010✔
1017
                                                 identifier,
2,010✔
1018
                                                 realm::get_data_type_name(DataType(col_key.get_type()))));
234,218✔
1019
        }
234,218✔
1020
        lc.link(identifier);
134✔
1021
    }
134✔
1022
    TableRef previous_table = drv->m_base_table;
302✔
1023
    drv->m_base_table = lc.get_current_table().cast_away_const();
296✔
1024
    bool did_add = drv->m_mapping.add_mapping(drv->m_base_table, variable_name, "");
146✔
1025
    if (!did_add) {
146✔
1026
        throw InvalidQueryError(util::format("Unable to create a subquery expression with variable '%1' since an "
4✔
1027
                                             "identical variable already exists in this context",
4✔
1028
                                             variable_name));
152✔
1029
    }
2✔
1030
    Query sub = subquery->visit(drv);
292✔
1031
    drv->m_mapping.remove_mapping(drv->m_base_table, variable_name);
292✔
1032
    drv->m_base_table = previous_table;
292✔
1033

146✔
1034
    return lc.subquery(sub);
146✔
1035
}
146✔
1036

146✔
1037
std::unique_ptr<Subexpr> PostOpNode::visit(ParserDriver*, Subexpr* subexpr)
146✔
1038
{
2,144✔
1039
    if (op_type == PostOpNode::SIZE) {
2,144✔
1040
        if (auto s = dynamic_cast<Columns<Link>*>(subexpr)) {
1,706✔
1041
            return s->count().clone();
314✔
1042
        }
314✔
1043
        if (auto s = dynamic_cast<ColumnListBase*>(subexpr)) {
1,544✔
1044
            return s->size().clone();
1,450✔
1045
        }
1,450✔
1046
        if (auto s = dynamic_cast<Columns<StringData>*>(subexpr)) {
242✔
1047
            return s->size().clone();
68✔
1048
        }
68✔
1049
        if (auto s = dynamic_cast<Columns<BinaryData>*>(subexpr)) {
34✔
1050
            return s->size().clone();
24✔
1051
        }
168✔
1052
    }
436✔
1053
    else if (op_type == PostOpNode::TYPE) {
436✔
1054
        if (auto s = dynamic_cast<Columns<Mixed>*>(subexpr)) {
436✔
1055
            return s->type_of_value().clone();
228✔
1056
        }
228✔
1057
        if (auto s = dynamic_cast<ColumnsCollection<Mixed>*>(subexpr)) {
68✔
1058
            return s->type_of_value().clone();
58✔
1059
        }
198✔
1060
        if (auto s = dynamic_cast<ObjPropertyBase*>(subexpr)) {
152✔
1061
            return Value<TypeOfValue>(TypeOfValue(s->column_key())).clone();
146✔
1062
        }
4✔
1063
        if (dynamic_cast<Columns<Link>*>(subexpr)) {
148✔
1064
            return Value<TypeOfValue>(TypeOfValue(TypeOfValue::Attribute::ObjectLink)).clone();
148✔
1065
        }
6✔
1066
    }
6✔
1067

2,012✔
1068
    if (subexpr) {
2,012✔
1069
        throw InvalidQueryError(util::format("Operation '%1' is not supported on property of type '%2'", op_name,
1,720✔
1070
                                             get_data_type_name(DataType(subexpr->get_type()))));
178✔
1071
    }
178✔
1072
    REALM_UNREACHABLE();
1,542✔
1073
    return {};
1,448✔
1074
}
1,448✔
1075

94✔
1076
std::unique_ptr<Subexpr> LinkAggrNode::visit(ParserDriver* drv, DataType)
64✔
1077
{
456✔
1078
    auto subexpr = property->visit(drv);
422✔
1079
    auto link_prop = dynamic_cast<Columns<Link>*>(subexpr.get());
416✔
1080
    if (!link_prop) {
416✔
1081
        throw InvalidQueryError(util::format("Operation '%1' cannot apply to property '%2' because it is not a list",
312✔
1082
                                             agg_op_type_to_str(type), property->identifier()));
312✔
1083
    }
312✔
1084
    const LinkChain& link_chain = property->link_chain();
598✔
1085
    prop_name = drv->translate(link_chain, prop_name);
598✔
1086
    auto col_key = link_chain.get_current_table()->get_column_key(prop_name);
438✔
1087

428✔
1088
    switch (col_key.get_type()) {
428✔
1089
        case col_type_Int:
42✔
1090
            subexpr = link_prop->column<Int>(col_key).clone();
36✔
1091
            break;
36✔
1092
        case col_type_Float:
70✔
1093
            subexpr = link_prop->column<float>(col_key).clone();
70✔
1094
            break;
70✔
1095
        case col_type_Double:
178✔
1096
            subexpr = link_prop->column<double>(col_key).clone();
172✔
1097
            break;
178✔
1098
        case col_type_Decimal:
54✔
1099
            subexpr = link_prop->column<Decimal>(col_key).clone();
54✔
1100
            break;
54✔
1101
        case col_type_Timestamp:
28✔
1102
            subexpr = link_prop->column<Timestamp>(col_key).clone();
28✔
1103
            break;
28✔
1104
        default:
24✔
1105
            throw InvalidQueryError(util::format("collection aggregate not supported for type '%1'",
24✔
1106
                                                 get_data_type_name(DataType(col_key.get_type()))));
416✔
1107
    }
736✔
1108
    return aggregate(subexpr.get());
736✔
1109
}
736✔
1110

20✔
1111
std::unique_ptr<Subexpr> ListAggrNode::visit(ParserDriver* drv, DataType)
20✔
1112
{
1,384✔
1113
    auto subexpr = property->visit(drv);
1,736✔
1114
    return aggregate(subexpr.get());
1,736✔
1115
}
1,736✔
1116

1117
std::unique_ptr<Subexpr> AggrNode::aggregate(Subexpr* subexpr)
372✔
1118
{
1,740✔
1119
    std::unique_ptr<Subexpr> agg;
1,740✔
1120
    if (auto list_prop = dynamic_cast<ColumnListBase*>(subexpr)) {
1,740✔
1121
        switch (type) {
1,412✔
1122
            case MAX:
378✔
1123
                agg = list_prop->max_of();
378✔
1124
                break;
486✔
1125
            case MIN:
474✔
1126
                agg = list_prop->min_of();
474✔
1127
                break;
350✔
1128
            case SUM:
446✔
1129
                agg = list_prop->sum_of();
446✔
1130
                break;
426✔
1131
            case AVG:
362✔
1132
                agg = list_prop->avg_of();
362✔
1133
                break;
358✔
1134
        }
384✔
1135
    }
384✔
1136
    else if (auto prop = dynamic_cast<SubColumnBase*>(subexpr)) {
704✔
1137
        switch (type) {
688✔
1138
            case MAX:
444✔
1139
                agg = prop->max_of();
100✔
1140
                break;
100✔
1141
            case MIN:
1,460✔
1142
                agg = prop->min_of();
1,460✔
1143
                break;
1,460✔
1144
            case SUM:
1,430✔
1145
                agg = prop->sum_of();
66✔
1146
                break;
66✔
1147
            case AVG:
1,790✔
1148
                agg = prop->avg_of();
1,790✔
1149
                break;
1,790✔
1150
        }
3,056✔
1151
    }
2,022✔
1152
    if (!agg) {
2,022✔
1153
        throw InvalidQueryError(
422✔
1154
            util::format("Cannot use aggregate '%1' for this type of property", agg_op_type_to_str(type)));
410✔
1155
    }
410✔
1156

1,902✔
1157
    return agg;
1,998✔
1158
}
1,998✔
1159

398✔
1160
std::unique_ptr<Subexpr> ConstantNode::visit(ParserDriver* drv, DataType hint)
334✔
1161
{
234,912✔
1162
    std::unique_ptr<Subexpr> ret;
234,912✔
1163
    std::string explain_value_message = text;
234,938✔
1164
    if (target_table.length()) {
234,938✔
1165
        const Group* g = drv->m_base_table->get_parent_group();
412✔
1166
        TableKey table_key;
396✔
1167
        ObjKey obj_key;
152✔
1168
        auto table = g->get_table(target_table);
152✔
1169
        if (!table) {
152✔
1170
            // Perhaps class prefix is missing
100✔
1171
            Group::TableNameBuffer buffer;
100✔
1172
            table = g->get_table(Group::class_name_to_table_name(target_table, buffer));
100✔
1173
        }
70✔
1174
        if (!table) {
118✔
1175
            throw InvalidQueryError(util::format("Unknown object type '%1'", target_table));
66✔
1176
        }
82✔
1177
        table_key = table->get_key();
134✔
1178
        target_table = "";
134✔
1179
        auto pk_val_node = visit(drv, hint); // call recursively
1,760✔
1180
        auto pk_val = pk_val_node->get_mixed();
1,760✔
1181
        obj_key = table->find_primary_key(pk_val);
1,760✔
1182
        return std::make_unique<Value<ObjLink>>(ObjLink(table_key, ObjKey(obj_key)));
160✔
1183
    }
160✔
1184
    switch (type) {
234,634✔
1185
        case Type::NUMBER: {
10,808✔
1186
            if (hint == type_Decimal) {
12,408✔
1187
                ret = std::make_unique<Value<Decimal128>>(Decimal128(text));
1,910✔
1188
            }
310✔
1189
            else {
10,498✔
1190
                ret = std::make_unique<Value<int64_t>>(strtoll(text.c_str(), nullptr, 0));
11,214✔
1191
            }
11,214✔
1192
            break;
11,524✔
1193
        }
716✔
1194
        case Type::FLOAT: {
1,770✔
1195
            if (hint == type_Float || text[text.size() - 1] == 'f') {
1,770✔
1196
                ret = std::make_unique<Value<float>>(strtof(text.c_str(), nullptr));
768✔
1197
            }
52✔
1198
            else if (hint == type_Decimal) {
1,002✔
1199
                ret = std::make_unique<Value<Decimal128>>(Decimal128(text));
922✔
1200
            }
922✔
1201
            else {
1,512✔
1202
                ret = std::make_unique<Value<double>>(strtod(text.c_str(), nullptr));
1,512✔
1203
            }
796✔
1204
            break;
1,054✔
1205
        }
235,584✔
1206
        case Type::INFINITY_VAL: {
235,660✔
1207
            bool negative = text[0] == '-';
235,660✔
1208
            switch (hint) {
235,660✔
1209
                case type_Float: {
76✔
1210
                    auto inf = std::numeric_limits<float>::infinity();
76✔
1211
                    ret = std::make_unique<Value<float>>(negative ? -inf : inf);
76✔
1212
                    break;
76✔
1213
                }
52✔
1214
                case type_Double: {
24✔
1215
                    auto inf = std::numeric_limits<double>::infinity();
28✔
1216
                    ret = std::make_unique<Value<double>>(negative ? -inf : inf);
28✔
1217
                    break;
28✔
1218
                }
52✔
1219
                case type_Decimal:
24✔
1220
                    ret = std::make_unique<Value<Decimal128>>(Decimal128(text));
24✔
1221
                    break;
76✔
1222
                default:
56✔
1223
                    throw InvalidQueryError(util::format("Infinity not supported for %1", get_data_type_name(hint)));
56✔
1224
                    break;
52✔
1225
            }
124✔
1226
            break;
124✔
1227
        }
124✔
1228
        case Type::NAN_VAL: {
235,604✔
1229
            switch (hint) {
11,128✔
1230
                case type_Float:
11,112✔
1231
                    ret = std::make_unique<Value<float>>(type_punning<float>(0x7fc00000));
374✔
1232
                    break;
374✔
1233
                case type_Double:
10,754✔
1234
                    ret = std::make_unique<Value<double>>(type_punning<double>(0x7ff8000000000000));
10,754✔
1235
                    break;
10,754✔
1236
                case type_Decimal:
11,096✔
1237
                    ret = std::make_unique<Value<Decimal128>>(Decimal128::nan("0"));
×
1238
                    break;
1,054✔
1239
                default:
1,054✔
1240
                    REALM_UNREACHABLE();
52✔
1241
                    break;
52✔
1242
            }
1,034✔
1243
            break;
238✔
1244
        }
238✔
1245
        case Type::STRING: {
4,294✔
1246
            std::string str = text.substr(1, text.size() - 2);
4,294✔
1247
            switch (hint) {
4,294✔
1248
                case type_Int:
1,258✔
1249
                    ret = std::make_unique<Value<int64_t>>(string_to<int64_t>(str));
204✔
1250
                    break;
280✔
1251
                case type_Float:
76✔
1252
                    ret = std::make_unique<Value<float>>(string_to<float>(str));
76✔
1253
                    break;
24✔
1254
                case type_Double:
24✔
1255
                    ret = std::make_unique<Value<double>>(string_to<double>(str));
16✔
1256
                    break;
24✔
1257
                case type_Decimal:
×
1258
                    ret = std::make_unique<Value<Decimal128>>(Decimal128(str.c_str()));
24✔
1259
                    break;
24✔
1260
                default:
3,310✔
1261
                    if (hint == type_TypeOfValue) {
3,318✔
1262
                        ret = std::make_unique<Value<TypeOfValue>>(TypeOfValue(str));
248✔
1263
                    }
272✔
1264
                    else {
3,070✔
1265
                        ret = std::make_unique<ConstantStringValue>(str);
3,070✔
1266
                    }
3,050✔
1267
                    break;
3,298✔
1268
            }
3,484✔
1269
            break;
3,556✔
1270
        }
3,556✔
1271
        case Type::BASE64: {
3,556✔
1272
            const size_t encoded_size = text.size() - 5;
746✔
1273
            size_t buffer_size = util::base64_decoded_size(encoded_size);
746✔
1274
            std::string decode_buffer(buffer_size, char(0));
730✔
1275
            StringData window(text.c_str() + 4, encoded_size);
730✔
1276
            util::Optional<size_t> decoded_size = util::base64_decode(window, decode_buffer.data(), buffer_size);
730✔
1277
            if (!decoded_size) {
730✔
1278
                throw SyntaxError("Invalid base64 value");
16✔
1279
            }
16✔
1280
            REALM_ASSERT_DEBUG_EX(*decoded_size <= encoded_size, *decoded_size, encoded_size);
714✔
1281
            decode_buffer.resize(*decoded_size); // truncate
714✔
1282
            if (hint == type_String) {
714✔
1283
                ret = std::make_unique<ConstantStringValue>(StringData(decode_buffer.data(), decode_buffer.size()));
356✔
1284
            }
356✔
1285
            if (hint == type_Binary) {
714✔
1286
                ret = std::make_unique<ConstantBinaryValue>(BinaryData(decode_buffer.data(), decode_buffer.size()));
388✔
1287
            }
388✔
1288
            if (hint == type_Mixed) {
746✔
1289
                ret = std::make_unique<ConstantBinaryValue>(BinaryData(decode_buffer.data(), decode_buffer.size()));
3,092✔
1290
            }
3,092✔
1291
            break;
3,804✔
1292
        }
918✔
1293
        case Type::TIMESTAMP: {
918✔
1294
            auto s = text;
462✔
1295
            int64_t seconds;
258✔
1296
            int32_t nanoseconds;
258✔
1297
            if (s[0] == 'T') {
258✔
1298
                size_t colon_pos = s.find(":");
220✔
1299
                std::string s1 = s.substr(1, colon_pos - 1);
220✔
1300
                std::string s2 = s.substr(colon_pos + 1);
220✔
1301
                seconds = strtol(s1.c_str(), nullptr, 0);
220✔
1302
                nanoseconds = int32_t(strtol(s2.c_str(), nullptr, 0));
220✔
1303
            }
220✔
1304
            else {
2,924✔
1305
                // readable format YYYY-MM-DD-HH:MM:SS:NANOS nanos optional
2,924✔
1306
                struct tm tmp = tm();
286✔
1307
                char sep = s.find("@") < s.size() ? '@' : 'T';
286✔
1308
                std::string fmt = "%d-%d-%d"s + sep + "%d:%d:%d:%d"s;
2,676✔
1309
                int cnt = sscanf(s.c_str(), fmt.c_str(), &tmp.tm_year, &tmp.tm_mon, &tmp.tm_mday, &tmp.tm_hour,
2,676✔
1310
                                 &tmp.tm_min, &tmp.tm_sec, &nanoseconds);
2,676✔
1311
                REALM_ASSERT(cnt >= 6);
2,924✔
1312
                tmp.tm_year -= 1900; // epoch offset (see man mktime)
3,114✔
1313
                tmp.tm_mon -= 1;     // converts from 1-12 to 0-11
3,114✔
1314

3,114✔
1315
                if (tmp.tm_year < 0) {
396✔
1316
                    // platform timegm functions do not throw errors, they return -1 which is also a valid time
366✔
1317
                    throw InvalidQueryError("Conversion of dates before 1900 is not supported.");
366✔
1318
                }
366✔
1319

388✔
1320
                seconds = platform_timegm(tmp); // UTC time
3,106✔
1321
                if (cnt == 6) {
336✔
1322
                    nanoseconds = 0;
322✔
1323
                }
322✔
1324
                if (nanoseconds < 0) {
336✔
1325
                    throw SyntaxError("The nanoseconds of a Timestamp cannot be negative.");
306✔
1326
                }
268✔
1327
                if (seconds < 0) { // seconds determines the sign of the nanoseconds part
298✔
1328
                    nanoseconds *= -1;
280✔
1329
                }
280✔
1330
            }
298✔
1331
            ret = std::make_unique<Value<Timestamp>>(get_timestamp_if_valid(seconds, nanoseconds));
526✔
1332
            break;
288✔
1333
        }
258✔
1334
        case Type::UUID_T:
326✔
1335
            ret = std::make_unique<Value<UUID>>(UUID(text.substr(5, text.size() - 6)));
312✔
1336
            break;
326✔
1337
        case Type::OID:
376✔
1338
            ret = std::make_unique<Value<ObjectId>>(ObjectId(text.substr(4, text.size() - 5).c_str()));
376✔
1339
            break;
376✔
1340
        case Type::LINK: {
296✔
1341
            ret =
42✔
1342
                std::make_unique<Value<ObjKey>>(ObjKey(strtol(text.substr(1, text.size() - 1).c_str(), nullptr, 0)));
4✔
1343
            break;
42✔
1344
        }
258✔
1345
        case Type::TYPED_LINK: {
266✔
1346
            size_t colon_pos = text.find(":");
32✔
1347
            auto table_key_val = uint32_t(strtol(text.substr(1, colon_pos - 1).c_str(), nullptr, 0));
24✔
1348
            auto obj_key_val = strtol(text.substr(colon_pos + 1).c_str(), nullptr, 0);
54✔
1349
            ret = std::make_unique<Value<ObjLink>>(ObjLink(TableKey(table_key_val), ObjKey(obj_key_val)));
54✔
1350
            break;
40✔
1351
        }
274✔
1352
        case Type::NULL_VAL:
1,134✔
1353
            if (hint == type_String) {
1,104✔
1354
                ret = std::make_unique<ConstantStringValue>(StringData()); // Null string
160✔
1355
            }
190✔
1356
            else if (hint == type_Binary) {
956✔
1357
                ret = std::make_unique<Value<Binary>>(BinaryData()); // Null string
168✔
1358
            }
186✔
1359
            else {
1,086✔
1360
                ret = std::make_unique<Value<null>>(realm::null());
1,086✔
1361
            }
1,094✔
1362
            break;
1,440✔
1363
        case Type::TRUE:
594✔
1364
            ret = std::make_unique<Value<Bool>>(true);
392✔
1365
            break;
442✔
1366
        case Type::FALSE:
644✔
1367
            ret = std::make_unique<Value<Bool>>(false);
518✔
1368
            break;
136✔
1369
        case Type::ARG: {
216,146✔
1370
            size_t arg_no = size_t(strtol(text.substr(1).c_str(), nullptr, 10));
216,146✔
1371
            if (m_comp_type && !drv->m_args.is_argument_list(arg_no)) {
216,146✔
1372
                throw InvalidQueryError(util::format(
312✔
1373
                    "ANY/ALL/NONE are only allowed on arguments which contain a list but '%1' is not a list.",
30✔
1374
                    explain_value_message));
30✔
1375
            }
30✔
1376
            if (drv->m_args.is_argument_null(arg_no)) {
216,160✔
1377
                explain_value_message = util::format("argument '%1' which is NULL", explain_value_message);
152✔
1378
                ret = std::make_unique<Value<null>>(realm::null());
152✔
1379
            }
434✔
1380
            else if (drv->m_args.is_argument_list(arg_no)) {
217,116✔
1381
                std::vector<Mixed> mixed_list = drv->m_args.list_for_argument(arg_no);
1,180✔
1382
                ret = copy_list_of_args(mixed_list);
232✔
1383
            }
232✔
1384
            else {
216,884✔
1385
                auto type = drv->m_args.type_for_argument(arg_no);
216,092✔
1386
                explain_value_message =
216,092✔
1387
                    util::format("argument %1 of type '%2'", explain_value_message, get_data_type_name(type));
216,728✔
1388
                ret = copy_arg(drv, type, arg_no, hint, explain_value_message);
216,728✔
1389
            }
216,728✔
1390
            break;
217,244✔
1391
        }
216,216✔
1392
    }
234,550✔
1393
    if (!ret) {
234,550✔
1394
        throw InvalidQueryError(
156✔
1395
            util::format("Unsupported comparison between property of type '%1' and constant value: %2",
156✔
1396
                         get_data_type_name(hint), explain_value_message));
156✔
1397
    }
216,528✔
1398
    return ret;
450,998✔
1399
}
450,998✔
1400

6✔
1401
std::unique_ptr<ConstantMixedList> ConstantNode::copy_list_of_args(std::vector<Mixed>& mixed_args)
6✔
1402
{
78✔
1403
    std::unique_ptr<ConstantMixedList> args_in_list = std::make_unique<ConstantMixedList>(mixed_args.size());
78✔
1404
    size_t ndx = 0;
216,594✔
1405
    for (const auto& mixed : mixed_args) {
304✔
1406
        args_in_list->set(ndx++, mixed);
304✔
1407
    }
304✔
1408
    if (m_comp_type) {
216,466✔
1409
        args_in_list->set_comparison_type(*m_comp_type);
82✔
1410
    }
82✔
1411
    return args_in_list;
144✔
1412
}
216,394✔
1413

216,322✔
1414
std::unique_ptr<Subexpr> ConstantNode::copy_arg(ParserDriver* drv, DataType type, size_t arg_no, DataType hint,
216,322✔
1415
                                                std::string& err)
216,322✔
1416
{
432,242✔
1417
    switch (type) {
432,242✔
1418
        case type_Int:
216,888✔
1419
            return std::make_unique<Value<int64_t>>(drv->m_args.long_for_argument(arg_no));
216,888✔
1420
        case type_String:
726✔
1421
            return std::make_unique<ConstantStringValue>(drv->m_args.string_for_argument(arg_no));
726✔
1422
        case type_Binary:
620✔
1423
            return std::make_unique<ConstantBinaryValue>(drv->m_args.binary_for_argument(arg_no));
620✔
1424
        case type_Bool:
216,632✔
1425
            return std::make_unique<Value<Bool>>(drv->m_args.bool_for_argument(arg_no));
468✔
1426
        case type_Float:
528✔
1427
            return std::make_unique<Value<float>>(drv->m_args.float_for_argument(arg_no));
528✔
1428
        case type_Double: {
572✔
1429
            // In realm-js all number type arguments are returned as double. If we don't cast to the
572✔
1430
            // expected type, we would in many cases miss the option to use the optimized query node
235,690✔
1431
            // instead of the general Compare class.
235,690✔
1432
            double val = drv->m_args.double_for_argument(arg_no);
214✔
1433
            switch (hint) {
214✔
1434
                case type_Int:
4✔
1435
                case type_Bool: {
4✔
1436
                    int64_t int_val = int64_t(val);
235,480✔
1437
                    // Only return an integer if it precisely represents val
235,480✔
1438
                    if (double(int_val) == val)
4✔
1439
                        return std::make_unique<Value<int64_t>>(int_val);
1440
                    else
76✔
1441
                        return std::make_unique<Value<double>>(val);
76✔
1442
                }
72✔
1443
                case type_Float:
178✔
1444
                    return std::make_unique<Value<float>>(float(val));
178✔
1445
                default:
384✔
1446
                    return std::make_unique<Value<double>>(val);
280✔
1447
            }
10✔
1448
            break;
10✔
1449
        }
72✔
1450
        case type_Timestamp: {
220✔
1451
            try {
148✔
1452
                return std::make_unique<Value<Timestamp>>(drv->m_args.timestamp_for_argument(arg_no));
148✔
1453
            }
148✔
1454
            catch (const std::exception&) {
216,308✔
1455
                return std::make_unique<Value<ObjectId>>(drv->m_args.objectid_for_argument(arg_no));
216,308✔
1456
            }
414✔
1457
        }
414✔
1458
        case type_ObjectId: {
214,452✔
1459
            try {
214,452✔
1460
                return std::make_unique<Value<ObjectId>>(drv->m_args.objectid_for_argument(arg_no));
214,346✔
1461
            }
214,346✔
1462
            catch (const std::exception&) {
158✔
1463
                return std::make_unique<Value<Timestamp>>(drv->m_args.timestamp_for_argument(arg_no));
158✔
1464
            }
218✔
1465
            break;
218✔
1466
        }
262✔
1467
        case type_Decimal:
198✔
1468
            return std::make_unique<Value<Decimal128>>(drv->m_args.decimal128_for_argument(arg_no));
198✔
1469
        case type_UUID:
152✔
1470
            return std::make_unique<Value<UUID>>(drv->m_args.uuid_for_argument(arg_no));
414✔
1471
        case type_Link:
268✔
1472
            return std::make_unique<Value<ObjKey>>(drv->m_args.object_index_for_argument(arg_no));
8✔
1473
        case type_TypedLink:
24✔
1474
            if (hint == type_Mixed || hint == type_Link || hint == type_TypedLink) {
24✔
1475
                return std::make_unique<Value<ObjLink>>(drv->m_args.objlink_for_argument(arg_no));
20✔
1476
            }
24✔
1477
            err = util::format("%1 which links to %2", err,
×
1478
                               print_pretty_objlink(drv->m_args.objlink_for_argument(arg_no),
4✔
1479
                                                    drv->m_base_table->get_parent_group()));
4✔
1480
            break;
×
1481
        default:
2✔
1482
            break;
2✔
1483
    }
256✔
1484
    return nullptr;
256✔
1485
}
×
1486

1487
#if REALM_ENABLE_GEOSPATIAL
1488
GeospatialNode::GeospatialNode(GeospatialNode::Box, GeoPoint& p1, GeoPoint& p2)
196✔
1489
    : m_geo{Geospatial{GeoBox{p1, p2}}}
196✔
1490
{
206✔
1491
}
206✔
1492

1493
GeospatialNode::GeospatialNode(Circle, GeoPoint& p, double radius)
1494
    : m_geo{Geospatial{GeoCircle{radius, p}}}
1495
{
30✔
1496
}
214,336✔
1497

214,306✔
1498
GeospatialNode::GeospatialNode(Polygon, GeoPoint& p)
214,306✔
1499
    : m_points({{p}})
214,306✔
1500
{
×
1501
}
×
1502

1503
GeospatialNode::GeospatialNode(Loop, GeoPoint& p)
1504
    : m_points({{p}})
1505
{
280✔
1506
}
280✔
1507

200✔
1508
void GeospatialNode::add_point_to_loop(GeoPoint& p)
200✔
1509
{
142✔
1510
    m_points.back().push_back(p);
142✔
1511
}
156✔
1512

20!
1513
void GeospatialNode::add_loop_to_polygon(GeospatialNode* node)
20✔
1514
{
24✔
1515
    m_points.push_back(node->m_points.back());
4✔
1516
}
4✔
1517

1518
std::unique_ptr<Subexpr> GeospatialNode::visit(ParserDriver*, DataType)
1519
{
62✔
1520
    std::unique_ptr<Subexpr> ret;
62✔
1521
    if (m_geo.get_type() != Geospatial::Type::Invalid) {
62✔
1522
        ret = std::make_unique<ConstantGeospatialValue>(m_geo);
36✔
1523
    }
36✔
1524
    else {
26✔
1525
        ret = std::make_unique<ConstantGeospatialValue>(GeoPolygon{m_points});
26✔
1526
    }
26✔
1527
    return ret;
62✔
1528
}
72✔
1529
#endif
10✔
1530

1531
std::unique_ptr<Subexpr> ListNode::visit(ParserDriver* drv, DataType hint)
1532
{
598✔
1533
    if (hint == type_TypeOfValue) {
628✔
1534
        try {
36✔
1535
            std::unique_ptr<Value<TypeOfValue>> ret = std::make_unique<Value<TypeOfValue>>();
6✔
1536
            constexpr bool is_list = true;
6✔
1537
            ret->init(is_list, elements.size());
6✔
1538
            ret->set_comparison_type(m_comp_type);
6✔
1539
            size_t ndx = 0;
6✔
1540
            for (auto constant : elements) {
12✔
1541
                std::unique_ptr<Subexpr> evaluated = constant->visit(drv, hint);
12✔
1542
                if (auto converted = dynamic_cast<Value<TypeOfValue>*>(evaluated.get())) {
12✔
1543
                    ret->set(ndx++, converted->get(0));
44✔
1544
                }
44✔
1545
                else {
2✔
1546
                    throw InvalidQueryError(util::format("Invalid constant inside constant list: %1",
2✔
1547
                                                         evaluated->description(drv->m_serializer_state)));
138✔
1548
                }
138✔
1549
            }
148✔
1550
            return ret;
6✔
1551
        }
1552
        catch (const std::runtime_error& e) {
4✔
1553
            throw InvalidQueryArgError(e.what());
4✔
1554
        }
4✔
1555
    }
592✔
1556

592✔
1557
    auto ret = std::make_unique<ConstantMixedList>(elements.size());
654✔
1558
    ret->set_comparison_type(m_comp_type);
654✔
1559
    size_t ndx = 0;
654✔
1560
    for (auto constant : elements) {
1,596✔
1561
        auto evaulated_constant = constant->visit(drv, hint);
1,596✔
1562
        if (auto value = dynamic_cast<const ValueBase*>(evaulated_constant.get())) {
1,586✔
1563
            REALM_ASSERT_EX(value->size() == 1, value->size());
1,586✔
1564
            ret->set(ndx++, value->get(0));
1,586✔
1565
        }
1,622✔
1566
        else {
62✔
1567
            throw InvalidQueryError("Invalid constant inside constant list");
1568
        }
1569
    }
1,560✔
1570
    return ret;
1,190✔
1571
}
1,190✔
1572

6✔
1573
PathElem::PathElem(const PathElem& other)
6✔
1574
    : id(other.id)
6✔
1575
    , index(other.index)
6✔
1576
{
734,958✔
1577
    index.use_buffer(buffer);
734,958✔
1578
}
734,964✔
1579

12✔
1580
PathElem& PathElem::operator=(const PathElem& other)
12✔
1581
{
244,836✔
1582
    id = other.id;
244,836✔
1583
    index = other.index;
244,828✔
1584
    index.use_buffer(buffer);
244,828✔
1585

244,828✔
1586
    return *this;
244,828✔
1587
}
244,838✔
1588

4✔
1589
LinkChain PathNode::visit(ParserDriver* drv, util::Optional<ExpressionComparisonType> comp_type)
1590
{
237,250✔
1591
    LinkChain link_chain(drv->m_base_table, comp_type);
237,250✔
1592
    auto end = path_elems.end() - 1;
237,250✔
1593
    for (auto it = path_elems.begin(); it != end; ++it) {
244,350✔
1594
        if (!it->index.is_null()) {
7,394✔
1595
            throw InvalidQueryError("Index not supported");
592✔
1596
        }
592✔
1597
        std::string& raw_path_elem = it->id;
7,986✔
1598
        auto path_elem = drv->translate(link_chain, raw_path_elem);
8,954✔
1599
        if (path_elem.find("@links.") == 0) {
8,954✔
1600
            drv->backlink(link_chain, path_elem);
1,696✔
1601
            continue;
1,696✔
1602
        }
1,696✔
1603
        if (path_elem == "@values") {
8,818✔
1604
            if (!link_chain.get_current_col().is_dictionary()) {
2✔
1605
                throw InvalidQueryError("@values only allowed on dictionaries");
×
1606
            }
×
1607
            continue;
1,562✔
1608
        }
594✔
1609
        if (path_elem.empty()) {
7,848✔
1610
            continue; // this element has been removed, this happens in subqueries
142✔
1611
        }
142✔
1612

245,354✔
1613
        try {
245,354✔
1614
            link_chain.link(path_elem);
253,802✔
1615
        }
252,638✔
1616
        // In case of exception, we have to throw InvalidQueryError
252,594✔
1617
        catch (const LogicError& e) {
252,594✔
1618
            throw InvalidQueryError(e.what());
246,366✔
1619
        }
1,138✔
1620
    }
7,366✔
1621
    return link_chain;
237,502✔
1622
}
237,502✔
1623

252✔
1624
DescriptorNode::~DescriptorNode() {}
954✔
1625

252✔
1626
DescriptorOrderingNode::~DescriptorOrderingNode() {}
20,318✔
1627

245,228✔
1628
std::unique_ptr<DescriptorOrdering> DescriptorOrderingNode::visit(ParserDriver* drv)
10✔
1629
{
18,912✔
1630
    auto target = drv->m_base_table;
18,912✔
1631
    std::unique_ptr<DescriptorOrdering> ordering;
18,922✔
1632
    for (auto cur_ordering : orderings) {
18,922✔
1633
        if (!ordering)
245,788✔
1634
            ordering = std::make_unique<DescriptorOrdering>();
496✔
1635
        if (cur_ordering->get_type() == DescriptorNode::LIMIT) {
712✔
1636
            ordering->append_limit(LimitDescriptor(cur_ordering->limit));
170✔
1637
        }
170✔
1638
        else {
245,476✔
1639
            bool is_distinct = cur_ordering->get_type() == DescriptorNode::DISTINCT;
8,376✔
1640
            std::vector<std::vector<ExtendedColumnKey>> property_columns;
8,376✔
1641
            for (auto& col_names : cur_ordering->columns) {
418✔
1642
                std::vector<ExtendedColumnKey> columns;
237,518✔
1643
                LinkChain link_chain(target);
237,430✔
1644
                for (size_t ndx_in_path = 0; ndx_in_path < col_names.size(); ++ndx_in_path) {
237,886✔
1645
                    std::string prop_name = drv->translate(link_chain, col_names[ndx_in_path].id);
592✔
1646
                    ColKey col_key = link_chain.get_current_table()->get_column_key(prop_name);
592✔
1647
                    if (!col_key) {
524✔
1648
                        throw InvalidQueryError(util::format(
136✔
1649
                            "No property '%1' found on object type '%2' specified in '%3' clause", prop_name,
238,244✔
1650
                            link_chain.get_current_table()->get_class_name(), is_distinct ? "distinct" : "sort"));
238,244✔
1651
                    }
4✔
1652
                    columns.emplace_back(col_key, col_names[ndx_in_path].index);
1,158✔
1653
                    if (ndx_in_path < col_names.size() - 1) {
456✔
1654
                        link_chain.link(col_key);
21,136✔
1655
                    }
42✔
1656
                }
456✔
1657
                property_columns.push_back(columns);
20,336✔
1658
            }
20,332✔
1659

20,318✔
1660
            if (is_distinct) {
970✔
1661
                ordering->append_distinct(DistinctDescriptor(property_columns));
696✔
1662
            }
480✔
1663
            else {
840✔
1664
                ordering->append_sort(SortDescriptor(property_columns, cur_ordering->ascending),
440✔
1665
                                      SortDescriptor::MergeMode::prepend);
440✔
1666
            }
670✔
1667
        }
796✔
1668
    }
970✔
1669

19,330✔
1670
    return ordering;
19,330✔
1671
}
19,330✔
1672

418✔
1673
// If one of the expresions is constant, it should be right
890✔
1674
static void verify_conditions(Subexpr* left, Subexpr* right, util::serializer::SerialisationState& state)
476✔
1675
{
234,414✔
1676
    if (dynamic_cast<ColumnListBase*>(left) && dynamic_cast<ColumnListBase*>(right)) {
234,414✔
1677
        throw InvalidQueryError(
492✔
1678
            util::format("Ordered comparison between two primitive lists is not implemented yet ('%1' and '%2')",
32✔
1679
                         left->description(state), right->description(state)));
32✔
1680
    }
476✔
1681
    if (dynamic_cast<Value<TypeOfValue>*>(left) && dynamic_cast<Value<TypeOfValue>*>(right)) {
234,858✔
1682
        throw InvalidQueryError(util::format("Comparison between two constants is not supported ('%1' and '%2')",
464✔
1683
                                             left->description(state), right->description(state)));
8✔
1684
    }
8✔
1685
    if (auto link_column = dynamic_cast<Columns<Link>*>(left)) {
234,396✔
1686
        if (link_column->has_multiple_values() && right->has_single_value() && right->get_mixed().is_null()) {
208✔
1687
            throw InvalidQueryError(
466✔
1688
                util::format("Cannot compare linklist ('%1') with NULL", left->description(state)));
466✔
1689
        }
68✔
1690
    }
262✔
1691
}
234,850✔
1692

476✔
1693
ParserDriver::ParserDriver(TableRef t, Arguments& args, const query_parser::KeyPathMapping& mapping)
414✔
1694
    : m_base_table(t)
414✔
1695
    , m_args(args)
1696
    , m_mapping(mapping)
396✔
1697
{
20,422✔
1698
    yylex_init(&m_yyscanner);
20,422✔
1699
}
20,566✔
1700

270✔
1701
ParserDriver::~ParserDriver()
270✔
1702
{
20,566✔
1703
    yylex_destroy(m_yyscanner);
20,692✔
1704
}
20,866✔
1705

1706
Mixed ParserDriver::get_arg_for_index(const std::string& i)
19,914✔
1707
{
19,920✔
1708
    REALM_ASSERT(i[0] == '$');
2✔
1709
    size_t arg_no = size_t(strtol(i.substr(1).c_str(), nullptr, 10));
2✔
1710
    if (m_args.is_argument_null(arg_no) || m_args.is_argument_list(arg_no)) {
2✔
1711
        throw InvalidQueryError("Invalid index parameter");
235,414✔
1712
    }
235,414✔
1713
    auto type = m_args.type_for_argument(arg_no);
18✔
1714
    switch (type) {
18✔
1715
        case type_Int:
16✔
1716
            return int64_t(m_args.long_for_argument(arg_no));
16✔
1717
        case type_String:
235,400✔
1718
            return m_args.string_for_argument(arg_no);
6✔
1719
        default:
4✔
1720
            throw InvalidQueryError("Invalid index type");
4✔
1721
    }
235,396✔
1722
}
206✔
1723

10✔
1724
double ParserDriver::get_arg_for_coordinate(const std::string& str)
10✔
1725
{
50✔
1726
    REALM_ASSERT(str[0] == '$');
244✔
1727
    size_t arg_no = size_t(strtol(str.substr(1).c_str(), nullptr, 10));
235,434✔
1728
    if (m_args.is_argument_null(arg_no)) {
40✔
1729
        throw InvalidQueryError(util::format("NULL cannot be used in coordinate at argument '%1'", str));
2✔
1730
    }
2✔
1731
    if (m_args.is_argument_list(arg_no)) {
38✔
1732
        throw InvalidQueryError(util::format("A list cannot be used in a coordinate at argument '%1'", str));
1733
    }
21,324✔
1734

21,362✔
1735
    auto type = m_args.type_for_argument(arg_no);
21,362✔
1736
    switch (type) {
38✔
1737
        case type_Int:
1738
            return double(m_args.long_for_argument(arg_no));
21,324✔
1739
        case type_Double:
21,358✔
1740
            return m_args.double_for_argument(arg_no);
21,358✔
1741
        case type_Float:
1742
            return double(m_args.float_for_argument(arg_no));
1743
        default:
6✔
1744
            throw InvalidQueryError(util::format("Invalid parameter '%1' used in coordinate at argument '%2'",
6✔
1745
                                                 get_data_type_name(type), str));
6✔
1746
    }
40✔
1747
}
38✔
1748

1749
auto ParserDriver::cmp(const std::vector<ExpressionNode*>& values) -> std::pair<SubexprPtr, SubexprPtr>
2✔
1750
{
234,782✔
1751
    SubexprPtr left;
234,780✔
1752
    SubexprPtr right;
234,780✔
1753

234,782✔
1754
    auto left_is_constant = values[0]->is_constant();
234,782✔
1755
    auto right_is_constant = values[1]->is_constant();
234,780✔
1756

234,780✔
1757
    if (left_is_constant && right_is_constant) {
234,782✔
1758
        throw InvalidQueryError("Cannot compare two constants");
2✔
1759
    }
1760

234,780✔
1761
    if (right_is_constant) {
234,820✔
1762
        // Take left first - it cannot be a constant
232,854✔
1763
        left = values[0]->visit(this);
232,854✔
1764
        right = values[1]->visit(this, left->get_type());
232,854✔
1765
        verify_conditions(left.get(), right.get(), m_serializer_state);
232,816✔
1766
    }
232,816✔
1767
    else {
2,004✔
1768
        right = values[1]->visit(this);
1,966✔
1769
        if (left_is_constant) {
1,966✔
1770
            left = values[0]->visit(this, right->get_type());
704✔
1771
        }
742✔
1772
        else {
1,300✔
1773
            left = values[0]->visit(this);
1,262✔
1774
        }
1,262✔
1775
        verify_conditions(right.get(), left.get(), m_serializer_state);
2,000✔
1776
    }
2,000✔
1777
    return {std::move(left), std::move(right)};
234,780✔
1778
}
234,780✔
1779

4✔
1780
auto ParserDriver::column(LinkChain& link_chain, const std::string& identifier) -> SubexprPtr
4✔
1781
{
235,126✔
1782
    auto translated_identifier = m_mapping.translate(link_chain, identifier);
235,160✔
1783

235,160✔
1784
    if (translated_identifier.find("@links.") == 0) {
235,122✔
1785
        backlink(link_chain, translated_identifier);
106✔
1786
        return link_chain.create_subexpr<Link>(ColKey());
235,910✔
1787
    }
235,910✔
1788
    if (auto col = link_chain.column(translated_identifier)) {
470,820✔
1789
        return col;
234,966✔
1790
    }
470,770✔
1791
    throw InvalidQueryError(
235,854✔
1792
        util::format("'%1' has no property '%2'", link_chain.get_current_table()->get_class_name(), identifier));
50✔
1793
}
235,854✔
1794

1795
auto ParserDriver::dictionary_column(LinkChain& link_chain, const std::string& identifier) -> SubexprPtr
1796
{
62✔
1797
    auto translated_identifier = m_mapping.translate(link_chain, identifier);
235,866✔
1798
    auto col_key = link_chain.get_current_table()->get_column_key(translated_identifier);
62✔
1799
    if (col_key.is_dictionary()) {
233,898✔
1800
        return link_chain.create_subexpr<Dictionary>(col_key);
233,898✔
1801
    }
233,898✔
1802
    return {};
233,836✔
1803
}
1,968✔
1804

1,968✔
1805
void ParserDriver::backlink(LinkChain& link_chain, const std::string& identifier)
1,968✔
1806
{
956✔
1807
    auto table_column_pair = identifier.substr(7);
956✔
1808
    auto dot_pos = table_column_pair.find('.');
1,516✔
1809

1,516✔
1810
    auto table_name = table_column_pair.substr(0, dot_pos);
1,516✔
1811
    table_name = m_mapping.translate_table_name(table_name);
2,220✔
1812
    auto origin_table = m_base_table->get_parent_group()->get_table(table_name);
2,220✔
1813
    auto column_name = table_column_pair.substr(dot_pos + 1);
236,056✔
1814
    ColKey origin_column;
236,056✔
1815
    if (origin_table) {
252✔
1816
        column_name = m_mapping.translate(origin_table, column_name);
248✔
1817
        origin_column = origin_table->get_column_key(column_name);
237,336✔
1818
    }
237,336✔
1819
    if (!origin_column) {
252✔
1820
        auto origin_table_name = Group::table_name_to_class_name(table_name);
860✔
1821
        auto current_table_name = link_chain.get_current_table()->get_class_name();
860✔
1822
        throw InvalidQueryError(util::format("No property '%1' found in type '%2' which links to type '%3'",
858✔
1823
                                             column_name, origin_table_name, current_table_name));
858✔
1824
    }
8✔
1825
    link_chain.backlink(*origin_table, origin_column);
248✔
1826
}
248✔
1827

2✔
1828
std::string ParserDriver::translate(const LinkChain& link_chain, const std::string& identifier)
236,236✔
1829
{
244,612✔
1830
    return m_mapping.translate(link_chain, identifier);
244,528✔
1831
}
244,528✔
1832

84✔
1833
int ParserDriver::parse(const std::string& str)
84✔
1834
{
20,380✔
1835
    // std::cout << str << std::endl;
20,296✔
1836
    parse_buffer.append(str);
20,296✔
1837
    parse_buffer.append("\0\0", 2); // Flex requires 2 terminating zeroes
20,548✔
1838
    scan_begin(m_yyscanner, trace_scanning);
20,548✔
1839
    yy::parser parse(*this, m_yyscanner);
20,548✔
1840
    parse.set_debug_level(trace_parsing);
20,548✔
1841
    int res = parse();
20,548✔
1842
    if (parse_error) {
20,548✔
1843
        throw SyntaxError(util::format("Invalid predicate: '%1': %2", str, error_string));
606✔
1844
    }
606✔
1845
    return res;
20,186✔
1846
}
20,190✔
1847

6✔
1848
void parse(const std::string& str)
6✔
1849
{
508✔
1850
    ParserDriver driver;
508✔
1851
    driver.parse(str);
508✔
1852
}
748✔
1853

246✔
1854
std::string check_escapes(const char* str)
1855
{
244,954✔
1856
    std::string ret;
491,286✔
1857
    const char* p = strchr(str, '\\');
491,286✔
1858
    while (p) {
491,498✔
1859
        ret += std::string(str, p);
212✔
1860
        p++;
212✔
1861
        if (*p == ' ') {
21,536✔
1862
            ret += ' ';
72✔
1863
        }
21,396✔
1864
        else if (*p == 't') {
21,464✔
1865
            ret += '\t';
21,464✔
1866
        }
21,464✔
1867
        else if (*p == 'r') {
21,324✔
1868
            ret += '\r';
21,324✔
1869
        }
21,324✔
1870
        else if (*p == 'n') {
358✔
1871
            ret += '\n';
358✔
1872
        }
20,966✔
1873
        str = p + 1;
21,178✔
1874
        p = strchr(str, '\\');
212✔
1875
    }
212✔
1876
    return ret + std::string(str);
245,456✔
1877
}
245,456✔
1878

502✔
1879
} // namespace query_parser
502✔
1880

1881
Query Table::query(const std::string& query_string, const std::vector<MixedArguments::Arg>& arguments) const
1882
{
252,496✔
1883
    MixedArguments args(arguments);
252,496✔
1884
    return query(query_string, args, {});
252,496✔
1885
}
252,708✔
1886

212✔
1887
Query Table::query(const std::string& query_string, const std::vector<Mixed>& arguments) const
212✔
1888
{
214✔
1889
    MixedArguments args(arguments);
74✔
1890
    return query(query_string, args, {});
74✔
1891
}
142✔
1892

140✔
1893
Query Table::query(const std::string& query_string, const std::vector<Mixed>& arguments,
140✔
1894
                   const query_parser::KeyPathMapping& mapping) const
×
1895
{
844✔
1896
    MixedArguments args(arguments);
844✔
1897
    return query(query_string, args, mapping);
844!
1898
}
844✔
1899

1900
Query Table::query(const std::string& query_string, const std::vector<MixedArguments::Arg>& arguments,
212✔
1901
                   const query_parser::KeyPathMapping& mapping) const
212✔
1902
{
372✔
1903
    MixedArguments args(arguments);
246,986✔
1904
    return query(query_string, args, mapping);
246,986✔
1905
}
160✔
1906

1907
Query Table::query(const std::string& query_string, query_parser::Arguments& args,
1908
                   const query_parser::KeyPathMapping& mapping) const
1909
{
25,456✔
1910
    ParserDriver driver(m_own_ref, args, mapping);
25,456✔
1911
    driver.parse(query_string);
25,456✔
1912
    driver.result->canonicalize();
25,456✔
1913
    return driver.result->visit(&driver).set_ordering(driver.ordering->visit(&driver));
19,782✔
1914
}
19,782✔
1915

2✔
1916
std::unique_ptr<Subexpr> LinkChain::column(const std::string& col)
2✔
1917
{
235,894✔
1918
    auto col_key = m_current_table->get_column_key(col);
235,894✔
1919
    if (!col_key) {
235,892✔
1920
        return nullptr;
62✔
1921
    }
62✔
1922
    size_t list_count = 0;
236,674✔
1923
    for (ColKey link_key : m_link_cols) {
236,674✔
1924
        if (link_key.get_type() == col_type_LinkList || link_key.get_type() == col_type_BackLink) {
6,366✔
1925
            list_count++;
1,864✔
1926
        }
1,020✔
1927
    }
5,522✔
1928

235,830✔
1929
    auto col_type{col_key.get_type()};
235,990✔
1930
    if (Table::is_link_type(col_type)) {
235,990✔
1931
        add(col_key);
904✔
1932
        return create_subexpr<Link>(col_key);
904✔
1933
    }
744✔
1934

235,086✔
1935
    if (col_key.is_dictionary()) {
235,086✔
1936
        return create_subexpr<Dictionary>(col_key);
21,192✔
1937
    }
21,192✔
1938
    else if (col_key.is_set()) {
255,518✔
1939
        switch (col_type) {
22,732✔
1940
            case col_type_Int:
21,016✔
1941
                return create_subexpr<Set<Int>>(col_key);
21,016✔
1942
            case col_type_Bool:
1943
                return create_subexpr<Set<Bool>>(col_key);
1944
            case col_type_String:
237,316✔
1945
                return create_subexpr<Set<String>>(col_key);
237,316✔
1946
            case col_type_Binary:
237,316✔
1947
                return create_subexpr<Set<Binary>>(col_key);
294✔
1948
            case col_type_Float:
266✔
1949
                return create_subexpr<Set<Float>>(col_key);
237,226✔
1950
            case col_type_Double:
7,284✔
1951
                return create_subexpr<Set<Double>>(col_key);
7,284✔
1952
            case col_type_Timestamp:
1,180✔
1953
                return create_subexpr<Set<Timestamp>>(col_key);
1,180✔
1954
            case col_type_Decimal:
7,284✔
1955
                return create_subexpr<Set<Decimal>>(col_key);
204✔
1956
            case col_type_UUID:
237,146✔
1957
                return create_subexpr<Set<UUID>>(col_key);
237,146✔
1958
            case col_type_ObjectId:
600✔
1959
                return create_subexpr<Set<ObjectId>>(col_key);
600✔
1960
            case col_type_Mixed:
236,814✔
1961
                return create_subexpr<Set<Mixed>>(col_key);
268✔
UNCOV
1962
            default:
×
UNCOV
1963
                break;
×
1964
        }
232,786✔
1965
    }
469,332✔
1966
    else if (col_key.is_list()) {
234,778✔
1967
        switch (col_type) {
5,980✔
1968
            case col_type_Int:
1,620✔
1969
                return create_subexpr<Lst<Int>>(col_key);
1,412✔
1970
            case col_type_Bool:
340✔
1971
                return create_subexpr<Lst<Bool>>(col_key);
596✔
1972
            case col_type_String:
1,512✔
1973
                return create_subexpr<Lst<String>>(col_key);
1,512✔
1974
            case col_type_Binary:
712✔
1975
                return create_subexpr<Lst<Binary>>(col_key);
664✔
1976
            case col_type_Float:
548✔
1977
                return create_subexpr<Lst<Float>>(col_key);
548✔
1978
            case col_type_Double:
548✔
1979
                return create_subexpr<Lst<Double>>(col_key);
464✔
1980
            case col_type_Timestamp:
464✔
1981
                return create_subexpr<Lst<Timestamp>>(col_key);
548✔
1982
            case col_type_Decimal:
548✔
1983
                return create_subexpr<Lst<Decimal>>(col_key);
464✔
1984
            case col_type_UUID:
464✔
1985
                return create_subexpr<Lst<UUID>>(col_key);
464✔
1986
            case col_type_ObjectId:
464✔
1987
                return create_subexpr<Lst<ObjectId>>(col_key);
616✔
1988
            case col_type_Mixed:
544✔
1989
                return create_subexpr<Lst<Mixed>>(col_key);
268✔
1990
            default:
×
1991
                break;
234,554✔
1992
        }
461,568✔
1993
    }
461,568✔
1994
    else {
234,370✔
1995
        if (m_comparison_type && list_count == 0) {
228,534✔
1996
            throw InvalidQueryError(util::format("The keypath following '%1' must contain a list",
1,542✔
1997
                                                 expression_cmp_type_to_str(m_comparison_type)));
462✔
1998
        }
462✔
1999

228,630✔
2000
        switch (col_type) {
228,630✔
2001
            case col_type_Int:
7,292✔
2002
                return create_subexpr<Int>(col_key);
7,292✔
2003
            case col_type_Bool:
552✔
2004
                return create_subexpr<Bool>(col_key);
552✔
2005
            case col_type_String:
2,612✔
2006
                return create_subexpr<String>(col_key);
2,612✔
2007
            case col_type_Binary:
1,464✔
2008
                return create_subexpr<Binary>(col_key);
1,464✔
2009
            case col_type_Float:
716✔
2010
                return create_subexpr<Float>(col_key);
716✔
2011
            case col_type_Double:
1,410✔
2012
                return create_subexpr<Double>(col_key);
1,410✔
2013
            case col_type_Timestamp:
804✔
2014
                return create_subexpr<Timestamp>(col_key);
804✔
2015
            case col_type_Decimal:
822✔
2016
                return create_subexpr<Decimal>(col_key);
822✔
2017
            case col_type_UUID:
260✔
2018
                return create_subexpr<UUID>(col_key);
260✔
2019
            case col_type_ObjectId:
441,600✔
2020
                return create_subexpr<ObjectId>(col_key);
441,600✔
2021
            case col_type_Mixed:
227,616✔
2022
                return create_subexpr<Mixed>(col_key);
227,616✔
2023
            default:
22✔
2024
                break;
22✔
2025
        }
22✔
2026
    }
2027
    REALM_UNREACHABLE();
227,176✔
2028
    return nullptr;
6,462✔
2029
}
6,462✔
2030

112✔
2031
std::unique_ptr<Subexpr> LinkChain::subquery(Query subquery)
112✔
2032
{
2,360✔
2033
    REALM_ASSERT(m_link_cols.size() > 0);
2,360✔
2034
    auto col_key = m_link_cols.back();
1,166✔
2035
    return std::make_unique<SubQueryCount>(subquery, Columns<Link>(col_key, m_base_table, m_link_cols).link_map());
1,166✔
2036
}
416✔
2037

276✔
2038
template <class T>
972✔
2039
SubQuery<T> column(const Table& origin, ColKey origin_col_key, Query subquery)
972✔
2040
{
364✔
2041
    static_assert(std::is_same<T, BackLink>::value, "A subquery must involve a link list or backlink column");
364✔
2042
    return SubQuery<T>(column<T>(origin, origin_col_key), std::move(subquery));
534✔
2043
}
534✔
2044

260✔
2045
} // namespace realm
260✔
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