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

realm / realm-core / 2103

06 Mar 2024 03:32PM UTC coverage: 90.949% (+0.02%) from 90.931%
2103

push

Evergreen

web-flow
Merge pull request #7419 from realm/je/logging-c-api

Add ability to get logging level via C API.
Add ability to get all category names

93928 of 173072 branches covered (54.27%)

38 of 39 new or added lines in 3 files covered. (97.44%)

104 existing lines in 21 files now uncovered.

238407 of 262133 relevant lines covered (90.95%)

5612447.62 hits per line

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

87.06
/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
#include "realm/object-store/class.hpp"
28

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

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

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

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

44
namespace {
45

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

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

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

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

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

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

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

165
template <>
166
inline Decimal128 string_to<Decimal128>(const std::string& s)
167
{
4✔
168
    Decimal128 value(s);
4✔
169
    if (value.is_nan()) {
4✔
170
        throw InvalidQueryArgError(util::format("Cannot convert '%1' to a %2", s, get_type_name<Decimal128>()));
4✔
171
    }
4✔
172
    return value;
×
173
}
×
174

175
class MixedArguments : public query_parser::Arguments {
176
public:
177
    using Arg = mpark::variant<Mixed, std::vector<Mixed>>;
178

179
    MixedArguments(const std::vector<Mixed>& args)
180
        : Arguments(args.size())
181
        , m_args([](const std::vector<Mixed>& args) -> std::vector<Arg> {
1,696✔
182
            std::vector<Arg> ret;
1,696✔
183
            ret.reserve(args.size());
1,696✔
184
            for (const Mixed& m : args) {
15,164✔
185
                ret.push_back(m);
15,164✔
186
            }
15,164✔
187
            return ret;
1,696✔
188
        }(args))
1,696✔
189
    {
1,696✔
190
    }
1,696✔
191
    MixedArguments(const std::vector<Arg>& args)
192
        : Arguments(args.size())
193
        , m_args(args)
194
    {
11,910✔
195
    }
11,910✔
196
    bool bool_for_argument(size_t n) final
197
    {
×
198
        return mixed_for_argument(n).get<bool>();
×
199
    }
×
200
    long long long_for_argument(size_t n) final
201
    {
×
202
        return mixed_for_argument(n).get<int64_t>();
×
203
    }
×
204
    float float_for_argument(size_t n) final
205
    {
×
206
        return mixed_for_argument(n).get<float>();
×
207
    }
×
208
    double double_for_argument(size_t n) final
209
    {
68✔
210
        return mixed_for_argument(n).get<double>();
68✔
211
    }
68✔
212
    StringData string_for_argument(size_t n) final
213
    {
20✔
214
        return mixed_for_argument(n).get<StringData>();
20✔
215
    }
20✔
216
    BinaryData binary_for_argument(size_t n) final
217
    {
×
218
        return mixed_for_argument(n).get<BinaryData>();
×
219
    }
×
220
    Timestamp timestamp_for_argument(size_t n) final
221
    {
×
222
        return mixed_for_argument(n).get<Timestamp>();
×
223
    }
×
224
    ObjectId objectid_for_argument(size_t n) final
225
    {
×
226
        return mixed_for_argument(n).get<ObjectId>();
×
227
    }
×
228
    UUID uuid_for_argument(size_t n) final
229
    {
×
230
        return mixed_for_argument(n).get<UUID>();
×
231
    }
×
232
    Decimal128 decimal128_for_argument(size_t n) final
233
    {
×
234
        return mixed_for_argument(n).get<Decimal128>();
×
235
    }
×
236
    ObjKey object_index_for_argument(size_t n) final
237
    {
×
238
        return mixed_for_argument(n).get<ObjKey>();
×
239
    }
×
240
    ObjLink objlink_for_argument(size_t n) final
241
    {
×
242
        return mixed_for_argument(n).get<ObjLink>();
×
243
    }
×
244
#if REALM_ENABLE_GEOSPATIAL
245
    Geospatial geospatial_for_argument(size_t n) final
246
    {
16✔
247
        return mixed_for_argument(n).get<Geospatial>();
16✔
248
    }
16✔
249
#endif
250
    std::vector<Mixed> list_for_argument(size_t n) final
251
    {
100✔
252
        Arguments::verify_ndx(n);
100✔
253
        return mpark::get<std::vector<Mixed>>(m_args[n]);
100✔
254
    }
100✔
255
    bool is_argument_null(size_t n) final
256
    {
428,816✔
257
        Arguments::verify_ndx(n);
428,816✔
258
        return visit(util::overload{
428,816✔
259
                         [](const Mixed& m) {
428,816✔
260
                             return m.is_null();
428,816✔
261
                         },
428,816✔
262
                         [](const std::vector<Mixed>&) {
214,408✔
263
                             return false;
×
264
                         },
×
265
                     },
428,816✔
266
                     m_args[n]);
428,816✔
267
    }
428,816✔
268
    bool is_argument_list(size_t n) final
269
    {
857,744✔
270
        Arguments::verify_ndx(n);
857,744✔
271
        static_assert(std::is_same_v<mpark::variant_alternative_t<1, Arg>, std::vector<Mixed>>);
857,744✔
272
        return m_args[n].index() == 1;
857,744✔
273
    }
857,744✔
274
    DataType type_for_argument(size_t n) final
275
    {
120✔
276
        return mixed_for_argument(n).get_type();
120✔
277
    }
120✔
278

279
    Mixed mixed_for_argument(size_t n) final
280
    {
428,868✔
281
        Arguments::verify_ndx(n);
428,868✔
282
        if (is_argument_list(n)) {
428,868✔
283
            throw InvalidQueryArgError(
×
284
                util::format("Request for scalar argument at index %1 but a list was provided", n));
×
285
        }
×
286

214,434✔
287
        return mpark::get<Mixed>(m_args[n]);
428,868✔
288
    }
428,868✔
289

290
private:
291
    const std::vector<Arg> m_args;
292
};
293

294
Timestamp get_timestamp_if_valid(int64_t seconds, int32_t nanoseconds)
295
{
596✔
296
    const bool both_non_negative = seconds >= 0 && nanoseconds >= 0;
596✔
297
    const bool both_non_positive = seconds <= 0 && nanoseconds <= 0;
596✔
298
    if (both_non_negative || both_non_positive) {
596✔
299
        return Timestamp(seconds, nanoseconds);
580✔
300
    }
580✔
301
    throw SyntaxError("Invalid timestamp format");
16✔
302
}
16✔
303

304
} // namespace
305

306
namespace realm {
307

308
namespace query_parser {
309

310
std::string_view string_for_op(CompareType op)
311
{
3,052✔
312
    switch (op) {
3,052✔
313
        case CompareType::EQUAL:
164✔
314
            return "=";
164✔
315
        case CompareType::NOT_EQUAL:
40✔
316
            return "!=";
40✔
317
        case CompareType::GREATER:
✔
318
            return ">";
×
319
        case CompareType::LESS:
✔
320
            return "<";
×
321
        case CompareType::GREATER_EQUAL:
✔
322
            return ">=";
×
323
        case CompareType::LESS_EQUAL:
✔
324
            return "<=";
×
325
        case CompareType::BEGINSWITH:
632✔
326
            return "beginswith";
632✔
327
        case CompareType::ENDSWITH:
616✔
328
            return "endswith";
616✔
329
        case CompareType::CONTAINS:
968✔
330
            return "contains";
968✔
331
        case CompareType::LIKE:
584✔
332
            return "like";
584✔
333
        case CompareType::IN:
8✔
334
            return "in";
8✔
335
        case CompareType::TEXT:
40✔
336
            return "text";
40✔
337
    }
×
338
    return ""; // appease MSVC warnings
×
339
}
×
340

341
NoArguments ParserDriver::s_default_args;
342
query_parser::KeyPathMapping ParserDriver::s_default_mapping;
343

344
ParserNode::~ParserNode() = default;
2,404,364✔
345

346
QueryNode::~QueryNode() = default;
908,956✔
347

348
Query NotNode::visit(ParserDriver* drv)
349
{
1,232✔
350
    Query q = drv->m_base_table->where();
1,232✔
351
    q.Not();
1,232✔
352
    q.and_query(query->visit(drv));
1,232✔
353
    return {q};
1,232✔
354
}
1,232✔
355

356
Query OrNode::visit(ParserDriver* drv)
357
{
556✔
358
    Query q(drv->m_base_table);
556✔
359
    q.group();
556✔
360
    for (auto it : children) {
430,476✔
361
        q.Or();
430,476✔
362
        q.and_query(it->visit(drv));
430,476✔
363
    }
430,476✔
364
    q.end_group();
556✔
365

278✔
366
    return q;
556✔
367
}
556✔
368

369
Query AndNode::visit(ParserDriver* drv)
370
{
708✔
371
    Query q(drv->m_base_table);
708✔
372
    for (auto it : children) {
1,592✔
373
        q.and_query(it->visit(drv));
1,592✔
374
    }
1,592✔
375
    return q;
708✔
376
}
708✔
377

378
static void verify_only_string_types(DataType type, std::string_view op_string)
379
{
3,052✔
380
    if (type != type_String && type != type_Binary && type != type_Mixed) {
3,052✔
381
        throw InvalidQueryError(util::format(
348✔
382
            "Unsupported comparison operator '%1' against type '%2', right side must be a string or binary type",
348✔
383
            op_string, get_data_type_name(type)));
348✔
384
    }
348✔
385
}
3,052✔
386

387
std::unique_ptr<Subexpr> OperationNode::visit(ParserDriver* drv, DataType type)
388
{
1,056✔
389
    std::unique_ptr<Subexpr> left;
1,056✔
390
    std::unique_ptr<Subexpr> right;
1,056✔
391

528✔
392
    const bool left_is_constant = m_left->is_constant();
1,056✔
393
    const bool right_is_constant = m_right->is_constant();
1,056✔
394
    const bool produces_multiple_values = m_left->is_list() || m_right->is_list();
1,056✔
395

528✔
396
    if (left_is_constant && right_is_constant && !produces_multiple_values) {
1,056✔
397
        right = m_right->visit(drv, type);
48✔
398
        left = m_left->visit(drv, type);
48✔
399
        auto v_left = left->get_mixed();
48✔
400
        auto v_right = right->get_mixed();
48✔
401
        Mixed result;
48✔
402
        switch (m_op) {
48✔
403
            case '+':
28✔
404
                result = v_left + v_right;
28✔
405
                break;
28✔
406
            case '-':
✔
407
                result = v_left - v_right;
×
408
                break;
×
409
            case '*':
20✔
410
                result = v_left * v_right;
20✔
411
                break;
20✔
412
            case '/':
✔
413
                result = v_left / v_right;
×
414
                break;
×
415
            default:
✔
416
                break;
×
417
        }
48✔
418
        return std::make_unique<Value<Mixed>>(result);
48✔
419
    }
48✔
420

504✔
421
    if (right_is_constant) {
1,008✔
422
        // Take left first - it cannot be a constant
208✔
423
        left = m_left->visit(drv);
416✔
424

208✔
425
        right = m_right->visit(drv, left->get_type());
416✔
426
    }
416✔
427
    else {
592✔
428
        right = m_right->visit(drv);
592✔
429
        if (left_is_constant) {
592✔
430
            left = m_left->visit(drv, right->get_type());
152✔
431
        }
152✔
432
        else {
440✔
433
            left = m_left->visit(drv);
440✔
434
        }
440✔
435
    }
592✔
436
    if (!Mixed::is_numeric(left->get_type(), right->get_type())) {
1,008✔
437
        util::serializer::SerialisationState state;
16✔
438
        std::string op(&m_op, 1);
16✔
439
        throw InvalidQueryArgError(util::format("Cannot perform '%1' operation on '%2' and '%3'", op,
16✔
440
                                                left->description(state), right->description(state)));
16✔
441
    }
16✔
442

496✔
443
    switch (m_op) {
992✔
444
        case '+':
400✔
445
            return std::make_unique<Operator<Plus>>(std::move(left), std::move(right));
400✔
446
        case '-':
136✔
447
            return std::make_unique<Operator<Minus>>(std::move(left), std::move(right));
136✔
448
        case '*':
260✔
449
            return std::make_unique<Operator<Mul>>(std::move(left), std::move(right));
260✔
450
        case '/':
196✔
451
            return std::make_unique<Operator<Div>>(std::move(left), std::move(right));
196✔
452
        default:
✔
453
            break;
×
454
    }
×
455
    return {};
×
456
}
×
457

458
Query EqualityNode::visit(ParserDriver* drv)
459
{
466,710✔
460
    auto [left, right] = drv->cmp(values);
466,710✔
461

233,344✔
462
    auto left_type = left->get_type();
466,710✔
463
    auto right_type = right->get_type();
466,710✔
464

233,344✔
465
    auto handle_typed_links = [drv](std::unique_ptr<Subexpr>& list, std::unique_ptr<Subexpr>& expr, DataType& type) {
233,544✔
466
        if (auto link_column = dynamic_cast<const Columns<Link>*>(list.get())) {
400✔
467
            // Change all TypedLink values to ObjKey values
200✔
468
            auto value = dynamic_cast<ValueBase*>(expr.get());
400✔
469
            auto left_dest_table_key = link_column->link_map().get_target_table()->get_key();
400✔
470
            auto sz = value->size();
400✔
471
            auto obj_keys = std::make_unique<Value<ObjKey>>();
400✔
472
            obj_keys->init(expr->has_multiple_values(), sz);
400✔
473
            obj_keys->set_comparison_type(expr->get_comparison_type());
400✔
474
            for (size_t i = 0; i < sz; i++) {
808✔
475
                auto val = value->get(i);
416✔
476
                // i'th entry is already NULL
208✔
477
                if (!val.is_null()) {
416✔
478
                    TableKey right_table_key;
200✔
479
                    ObjKey right_obj_key;
200✔
480
                    if (val.is_type(type_Link)) {
200✔
481
                        right_table_key = left_dest_table_key;
20✔
482
                        right_obj_key = val.get<ObjKey>();
20✔
483
                    }
20✔
484
                    else if (val.is_type(type_TypedLink)) {
180✔
485
                        right_table_key = val.get_link().get_table_key();
180✔
486
                        right_obj_key = val.get_link().get_obj_key();
180✔
487
                    }
180✔
488
                    else {
×
489
                        const char* target_type = get_data_type_name(val.get_type());
×
490
                        throw InvalidQueryError(
×
491
                            util::format("Unsupported comparison between '%1' and type '%2'",
×
492
                                         link_column->link_map().description(drv->m_serializer_state), target_type));
×
493
                    }
×
494
                    if (left_dest_table_key == right_table_key) {
200✔
495
                        obj_keys->set(i, right_obj_key);
192✔
496
                    }
192✔
497
                    else {
8✔
498
                        const Group* g = drv->m_base_table->get_parent_group();
8✔
499
                        throw InvalidQueryArgError(
8✔
500
                            util::format("The relationship '%1' which links to type '%2' cannot be compared to "
8✔
501
                                         "an argument of type %3",
8✔
502
                                         link_column->link_map().description(drv->m_serializer_state),
8✔
503
                                         link_column->link_map().get_target_table()->get_class_name(),
8✔
504
                                         print_pretty_objlink(ObjLink(right_table_key, right_obj_key), g)));
8✔
505
                    }
8✔
506
                }
200✔
507
            }
416✔
508
            expr = std::move(obj_keys);
396✔
509
            type = type_Link;
392✔
510
        }
392✔
511
    };
400✔
512

233,344✔
513
    if (left_type == type_Link && right->has_constant_evaluation()) {
466,710✔
514
        handle_typed_links(left, right, right_type);
388✔
515
    }
388✔
516
    if (right_type == type_Link && left->has_constant_evaluation()) {
466,710✔
517
        handle_typed_links(right, left, left_type);
12✔
518
    }
12✔
519

233,344✔
520
    if (left_type.is_valid() && right_type.is_valid() && !Mixed::data_types_are_comparable(left_type, right_type)) {
466,710✔
521
        throw InvalidQueryError(util::format("Unsupported comparison between type '%1' and type '%2'",
16✔
522
                                             get_data_type_name(left_type), get_data_type_name(right_type)));
16✔
523
    }
16✔
524
    if (left_type == type_TypeOfValue || right_type == type_TypeOfValue) {
466,694✔
525
        if (left_type != right_type) {
568✔
526
            throw InvalidQueryArgError(
12✔
527
                util::format("Unsupported comparison between @type and raw value: '%1' and '%2'",
12✔
528
                             get_data_type_name(left_type), get_data_type_name(right_type)));
12✔
529
        }
12✔
530
    }
466,682✔
531

233,330✔
532
    if (op == CompareType::IN) {
466,682✔
533
        Subexpr* r = right.get();
696✔
534
        if (!r->has_multiple_values()) {
696✔
535
            throw InvalidQueryArgError("The keypath following 'IN' must contain a list. Found '" +
8✔
536
                                       r->description(drv->m_serializer_state) + "'");
8✔
537
        }
8✔
538
    }
466,674✔
539

233,326✔
540
    if (left_type == type_Link && left_type == right_type && right->has_constant_evaluation()) {
466,674✔
541
        if (auto link_column = dynamic_cast<const Columns<Link>*>(left.get())) {
380✔
542
            if (link_column->link_map().get_nb_hops() == 1 &&
380✔
543
                link_column->get_comparison_type().value_or(ExpressionComparisonType::Any) ==
352✔
544
                    ExpressionComparisonType::Any) {
316✔
545
                REALM_ASSERT(dynamic_cast<const Value<ObjKey>*>(right.get()));
308✔
546
                auto link_values = static_cast<const Value<ObjKey>*>(right.get());
308✔
547
                // We can use a LinksToNode based query
154✔
548
                std::vector<ObjKey> values;
308✔
549
                values.reserve(link_values->size());
308✔
550
                for (auto val : *link_values) {
324✔
551
                    values.emplace_back(val.is_null() ? ObjKey() : val.get<ObjKey>());
254✔
552
                }
324✔
553
                if (op == CompareType::EQUAL) {
308✔
554
                    return drv->m_base_table->where().links_to(link_column->link_map().get_first_column_key(),
200✔
555
                                                               values);
200✔
556
                }
200✔
557
                else if (op == CompareType::NOT_EQUAL) {
108✔
558
                    return drv->m_base_table->where().not_links_to(link_column->link_map().get_first_column_key(),
104✔
559
                                                                   values);
104✔
560
                }
104✔
561
            }
466,294✔
562
        }
380✔
563
    }
466,294✔
564
    else if (right->has_single_value() && (left_type == right_type || left_type == type_Mixed)) {
466,294✔
565
        Mixed val = right->get_mixed();
458,612✔
566
        const ObjPropertyBase* prop = dynamic_cast<const ObjPropertyBase*>(left.get());
458,612✔
567
        if (prop && !prop->links_exist() && !prop->has_path()) {
458,612✔
568
            auto col_key = prop->column_key();
443,550✔
569
            if (val.is_null()) {
443,550✔
570
                switch (op) {
212✔
571
                    case CompareType::EQUAL:
156✔
572
                    case CompareType::IN:
156✔
573
                        return drv->m_base_table->where().equal(col_key, realm::null());
156✔
574
                    case CompareType::NOT_EQUAL:
106✔
575
                        return drv->m_base_table->where().not_equal(col_key, realm::null());
56✔
576
                    default:
78✔
577
                        break;
×
578
                }
443,338✔
579
            }
443,338✔
580
            switch (left->get_type()) {
443,338✔
581
                case type_Int:
9,760✔
582
                    return drv->simple_query(op, col_key, val.get_int());
9,760✔
583
                case type_Bool:
132✔
584
                    return drv->simple_query(op, col_key, val.get_bool());
132✔
585
                case type_String:
2,392✔
586
                    return drv->simple_query(op, col_key, val.get_string(), case_sensitive);
2,392✔
587
                case type_Binary:
1,416✔
588
                    return drv->simple_query(op, col_key, val.get_binary(), case_sensitive);
1,416✔
589
                case type_Timestamp:
140✔
590
                    return drv->simple_query(op, col_key, val.get<Timestamp>());
140✔
591
                case type_Float:
52✔
592
                    return drv->simple_query(op, col_key, val.get_float());
52✔
593
                case type_Double:
100✔
594
                    return drv->simple_query(op, col_key, val.get_double());
100✔
595
                case type_Decimal:
704✔
596
                    return drv->simple_query(op, col_key, val.get<Decimal128>());
704✔
597
                case type_ObjectId:
428,384✔
598
                    return drv->simple_query(op, col_key, val.get<ObjectId>());
428,384✔
599
                case type_UUID:
164✔
600
                    return drv->simple_query(op, col_key, val.get<UUID>());
164✔
601
                case type_Mixed:
120✔
602
                    return drv->simple_query(op, col_key, val, case_sensitive);
120✔
603
                default:
✔
604
                    break;
×
605
            }
22,820✔
606
        }
22,820✔
607
    }
458,612✔
608
    if (case_sensitive) {
22,820✔
609
        switch (op) {
21,992✔
610
            case CompareType::EQUAL:
18,180✔
611
            case CompareType::IN:
18,520✔
612
                return Query(std::unique_ptr<Expression>(new Compare<Equal>(std::move(left), std::move(right))));
18,520✔
613
            case CompareType::NOT_EQUAL:
10,996✔
614
                return Query(std::unique_ptr<Expression>(new Compare<NotEqual>(std::move(left), std::move(right))));
3,472✔
615
            default:
9,260✔
616
                break;
×
617
        }
828✔
618
    }
828✔
619
    else {
828✔
620
        verify_only_string_types(right_type, util::format("%1%2", string_for_op(op), "[c]"));
828✔
621
        switch (op) {
828✔
622
            case CompareType::EQUAL:
108✔
623
            case CompareType::IN:
112✔
624
                return Query(std::unique_ptr<Expression>(new Compare<EqualIns>(std::move(left), std::move(right))));
112✔
625
            case CompareType::NOT_EQUAL:
76✔
626
                return Query(
40✔
627
                    std::unique_ptr<Expression>(new Compare<NotEqualIns>(std::move(left), std::move(right))));
40✔
628
            default:
56✔
629
                break;
×
630
        }
×
631
    }
×
632
    return {};
×
633
}
×
634

635
Query BetweenNode::visit(ParserDriver* drv)
636
{
92✔
637
    if (limits->elements.size() != 2) {
92✔
638
        throw InvalidQueryError("Operator 'BETWEEN' requires list with 2 elements.");
8✔
639
    }
8✔
640

42✔
641
    if (dynamic_cast<ColumnListBase*>(prop->visit(drv, type_Int).get())) {
84✔
642
        // It's a list!
8✔
643
        util::Optional<ExpressionComparisonType> cmp_type = dynamic_cast<PropertyNode*>(prop)->comp_type;
16✔
644
        if (cmp_type.value_or(ExpressionComparisonType::Any) != ExpressionComparisonType::All) {
16✔
645
            throw InvalidQueryError("Only 'ALL' supported for operator 'BETWEEN' when applied to lists.");
12✔
646
        }
12✔
647
    }
72✔
648

36✔
649
    auto& min(limits->elements.at(0));
72✔
650
    auto& max(limits->elements.at(1));
72✔
651
    RelationalNode cmp1(prop, CompareType::GREATER_EQUAL, min);
72✔
652
    RelationalNode cmp2(prop, CompareType::LESS_EQUAL, max);
72✔
653

36✔
654
    Query q(drv->m_base_table);
72✔
655
    q.and_query(cmp1.visit(drv));
72✔
656
    q.and_query(cmp2.visit(drv));
72✔
657

36✔
658
    return q;
72✔
659
}
72✔
660

661
Query RelationalNode::visit(ParserDriver* drv)
662
{
5,308✔
663
    auto [left, right] = drv->cmp(values);
5,308✔
664

2,654✔
665
    auto left_type = left->get_type();
5,308✔
666
    auto right_type = right->get_type();
5,308✔
667
    const bool right_type_is_null = right->has_single_value() && right->get_mixed().is_null();
5,308✔
668
    const bool left_type_is_null = left->has_single_value() && left->get_mixed().is_null();
5,308✔
669
    REALM_ASSERT(!(left_type_is_null && right_type_is_null));
5,308!
670

2,654✔
671
    if (left_type == type_Link || left_type == type_TypeOfValue) {
5,308✔
672
        throw InvalidQueryError(util::format(
×
673
            "Unsupported operator %1 in query. Only equal (==) and not equal (!=) are supported for this type.",
×
674
            string_for_op(op)));
×
675
    }
×
676

2,654✔
677
    if (!(left_type_is_null || right_type_is_null) && (!left_type.is_valid() || !right_type.is_valid() ||
5,308✔
678
                                                       !Mixed::data_types_are_comparable(left_type, right_type))) {
4,800✔
679
        throw InvalidQueryError(util::format("Unsupported comparison between type '%1' and type '%2'",
24✔
680
                                             get_data_type_name(left_type), get_data_type_name(right_type)));
24✔
681
    }
24✔
682

2,642✔
683
    const ObjPropertyBase* prop = dynamic_cast<const ObjPropertyBase*>(left.get());
5,284✔
684
    if (prop && !prop->links_exist() && !prop->has_path() && right->has_single_value() &&
5,284✔
685
        (left_type == right_type || left_type == type_Mixed)) {
3,564✔
686
        auto col_key = prop->column_key();
1,556✔
687
        switch (left->get_type()) {
1,556✔
688
            case type_Int:
692✔
689
                return drv->simple_query(op, col_key, right->get_mixed().get_int());
692✔
690
            case type_Bool:
✔
691
                break;
×
692
            case type_String:
92✔
693
                return drv->simple_query(op, col_key, right->get_mixed().get_string());
92✔
694
            case type_Binary:
✔
695
                break;
×
696
            case type_Timestamp:
48✔
697
                return drv->simple_query(op, col_key, right->get_mixed().get<Timestamp>());
48✔
698
            case type_Float:
68✔
699
                return drv->simple_query(op, col_key, right->get_mixed().get_float());
68✔
700
                break;
×
701
            case type_Double:
144✔
702
                return drv->simple_query(op, col_key, right->get_mixed().get_double());
144✔
703
                break;
×
704
            case type_Decimal:
16✔
705
                return drv->simple_query(op, col_key, right->get_mixed().get<Decimal128>());
16✔
706
                break;
×
707
            case type_ObjectId:
288✔
708
                return drv->simple_query(op, col_key, right->get_mixed().get<ObjectId>());
288✔
709
                break;
×
710
            case type_UUID:
128✔
711
                return drv->simple_query(op, col_key, right->get_mixed().get<UUID>());
128✔
712
                break;
×
713
            case type_Mixed:
80✔
714
                return drv->simple_query(op, col_key, right->get_mixed());
80✔
715
                break;
×
716
            default:
✔
717
                break;
×
718
        }
3,728✔
719
    }
3,728✔
720
    switch (op) {
3,728✔
721
        case CompareType::GREATER:
2,316✔
722
            return Query(std::unique_ptr<Expression>(new Compare<Greater>(std::move(left), std::move(right))));
2,316✔
723
        case CompareType::LESS:
284✔
724
            return Query(std::unique_ptr<Expression>(new Compare<Less>(std::move(left), std::move(right))));
284✔
725
        case CompareType::GREATER_EQUAL:
540✔
726
            return Query(std::unique_ptr<Expression>(new Compare<GreaterEqual>(std::move(left), std::move(right))));
540✔
727
        case CompareType::LESS_EQUAL:
172✔
728
            return Query(std::unique_ptr<Expression>(new Compare<LessEqual>(std::move(left), std::move(right))));
172✔
729
        default:
✔
730
            break;
×
731
    }
×
732
    return {};
×
733
}
×
734

735
Query StringOpsNode::visit(ParserDriver* drv)
736
{
2,856✔
737
    auto [left, right] = drv->cmp(values);
2,856✔
738

1,428✔
739
    auto left_type = left->get_type();
2,856✔
740
    auto right_type = right->get_type();
2,856✔
741
    const ObjPropertyBase* prop = dynamic_cast<const ObjPropertyBase*>(left.get());
2,856✔
742

1,428✔
743
    verify_only_string_types(right_type, string_for_op(op));
2,856✔
744

1,428✔
745
    if (prop && !prop->links_exist() && !prop->has_path() && right->has_single_value() &&
2,856✔
746
        (left_type == right_type || left_type == type_Mixed)) {
1,702✔
747
        auto col_key = prop->column_key();
548✔
748
        if (right_type == type_String) {
548✔
749
            StringData val = right->get_mixed().get_string();
332✔
750

166✔
751
            switch (op) {
332✔
752
                case CompareType::BEGINSWITH:
56✔
753
                    return drv->m_base_table->where().begins_with(col_key, val, case_sensitive);
56✔
754
                case CompareType::ENDSWITH:
64✔
755
                    return drv->m_base_table->where().ends_with(col_key, val, case_sensitive);
64✔
756
                case CompareType::CONTAINS:
120✔
757
                    return drv->m_base_table->where().contains(col_key, val, case_sensitive);
120✔
758
                case CompareType::LIKE:
56✔
759
                    return drv->m_base_table->where().like(col_key, val, case_sensitive);
56✔
760
                case CompareType::TEXT:
36✔
761
                    return drv->m_base_table->where().fulltext(col_key, val);
36✔
762
                case CompareType::IN:
✔
763
                case CompareType::EQUAL:
✔
764
                case CompareType::NOT_EQUAL:
✔
765
                case CompareType::GREATER:
✔
766
                case CompareType::LESS:
✔
767
                case CompareType::GREATER_EQUAL:
✔
768
                case CompareType::LESS_EQUAL:
✔
769
                    break;
×
770
            }
216✔
771
        }
216✔
772
        else if (right_type == type_Binary) {
216✔
773
            BinaryData val = right->get_mixed().get_binary();
216✔
774

108✔
775
            switch (op) {
216✔
776
                case CompareType::BEGINSWITH:
48✔
777
                    return drv->m_base_table->where().begins_with(col_key, val, case_sensitive);
48✔
778
                case CompareType::ENDSWITH:
56✔
779
                    return drv->m_base_table->where().ends_with(col_key, val, case_sensitive);
56✔
780
                case CompareType::CONTAINS:
72✔
781
                    return drv->m_base_table->where().contains(col_key, val, case_sensitive);
72✔
782
                case CompareType::LIKE:
40✔
783
                    return drv->m_base_table->where().like(col_key, val, case_sensitive);
40✔
784
                case CompareType::TEXT:
✔
785
                case CompareType::IN:
✔
786
                case CompareType::EQUAL:
✔
787
                case CompareType::NOT_EQUAL:
✔
788
                case CompareType::GREATER:
✔
789
                case CompareType::LESS:
✔
790
                case CompareType::GREATER_EQUAL:
✔
791
                case CompareType::LESS_EQUAL:
✔
792
                    break;
×
793
            }
2,308✔
794
        }
2,308✔
795
    }
548✔
796

1,154✔
797
    if (case_sensitive) {
2,308✔
798
        switch (op) {
732✔
799
            case CompareType::BEGINSWITH:
152✔
800
                return Query(std::unique_ptr<Expression>(new Compare<BeginsWith>(std::move(right), std::move(left))));
152✔
801
            case CompareType::ENDSWITH:
152✔
802
                return Query(std::unique_ptr<Expression>(new Compare<EndsWith>(std::move(right), std::move(left))));
152✔
803
            case CompareType::CONTAINS:
272✔
804
                return Query(std::unique_ptr<Expression>(new Compare<Contains>(std::move(right), std::move(left))));
272✔
805
            case CompareType::LIKE:
152✔
806
                return Query(std::unique_ptr<Expression>(new Compare<Like>(std::move(right), std::move(left))));
152✔
807
            case CompareType::TEXT: {
4✔
808
                StringData val = right->get_mixed().get_string();
4✔
809
                auto string_prop = dynamic_cast<Columns<StringData>*>(left.get());
4✔
810
                return string_prop->fulltext(val);
4✔
811
            }
×
812
            case CompareType::IN:
✔
813
            case CompareType::EQUAL:
✔
814
            case CompareType::NOT_EQUAL:
✔
815
            case CompareType::GREATER:
✔
816
            case CompareType::LESS:
✔
817
            case CompareType::GREATER_EQUAL:
✔
818
            case CompareType::LESS_EQUAL:
✔
819
                break;
×
820
        }
1,576✔
821
    }
1,576✔
822
    else {
1,576✔
823
        switch (op) {
1,576✔
824
            case CompareType::BEGINSWITH:
304✔
825
                return Query(
304✔
826
                    std::unique_ptr<Expression>(new Compare<BeginsWithIns>(std::move(right), std::move(left))));
304✔
827
            case CompareType::ENDSWITH:
272✔
828
                return Query(
272✔
829
                    std::unique_ptr<Expression>(new Compare<EndsWithIns>(std::move(right), std::move(left))));
272✔
830
            case CompareType::CONTAINS:
432✔
831
                return Query(
432✔
832
                    std::unique_ptr<Expression>(new Compare<ContainsIns>(std::move(right), std::move(left))));
432✔
833
            case CompareType::LIKE:
264✔
834
                return Query(std::unique_ptr<Expression>(new Compare<LikeIns>(std::move(right), std::move(left))));
264✔
835
            case CompareType::IN:
✔
836
            case CompareType::EQUAL:
✔
837
            case CompareType::NOT_EQUAL:
✔
838
            case CompareType::GREATER:
✔
839
            case CompareType::LESS:
✔
840
            case CompareType::GREATER_EQUAL:
✔
841
            case CompareType::LESS_EQUAL:
✔
842
            case CompareType::TEXT:
✔
843
                break;
×
844
        }
×
845
    }
×
846
    return {};
×
847
}
×
848

849
#if REALM_ENABLE_GEOSPATIAL
850
Query GeoWithinNode::visit(ParserDriver* drv)
851
{
176✔
852
    auto left = prop->visit(drv);
176✔
853
    auto left_type = left->get_type();
176✔
854
    if (left_type != type_Link) {
176✔
855
        throw InvalidQueryError(util::format("The left hand side of 'geoWithin' must be a link to geoJSON formatted "
4✔
856
                                             "data. But the provided type is '%1'",
4✔
857
                                             get_data_type_name(left_type)));
4✔
858
    }
4✔
859
    auto link_column = dynamic_cast<const Columns<Link>*>(left.get());
172✔
860

86✔
861
    if (geo) {
172✔
862
        auto right = geo->visit(drv, type_Int);
124✔
863
        auto geo_value = dynamic_cast<const ConstantGeospatialValue*>(right.get());
124✔
864
        return link_column->geo_within(geo_value->get_mixed().get<Geospatial>());
124✔
865
    }
124✔
866

24✔
867
    REALM_ASSERT_3(argument.size(), >, 1);
48✔
868
    REALM_ASSERT_3(argument[0], ==, '$');
48✔
869
    size_t arg_no = size_t(strtol(argument.substr(1).c_str(), nullptr, 10));
48✔
870
    auto right_type = drv->m_args.is_argument_null(arg_no) ? DataType(-1) : drv->m_args.type_for_argument(arg_no);
46✔
871

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

14✔
915
    if (geo_from_argument.get_type() == Geospatial::Type::Invalid) {
28✔
916
        throw InvalidQueryError(util::format(
4✔
917
            "The right hand side of 'geoWithin' must be a valid Geospatial value, got '%1'", geo_from_argument));
4✔
918
    }
4✔
919
    Status geo_status = geo_from_argument.is_valid();
24✔
920
    if (!geo_status.is_ok()) {
24✔
921
        throw InvalidQueryError(
×
922
            util::format("The Geospatial query argument region is invalid: '%1'", geo_status.reason()));
×
923
    }
×
924
    return link_column->geo_within(geo_from_argument);
24✔
925
}
24✔
926
#endif
927

928
Query TrueOrFalseNode::visit(ParserDriver* drv)
929
{
612✔
930
    Query q = drv->m_base_table->where();
612✔
931
    if (true_or_false) {
612✔
932
        q.and_query(std::unique_ptr<realm::Expression>(new TrueExpression));
460✔
933
    }
460✔
934
    else {
152✔
935
        q.and_query(std::unique_ptr<realm::Expression>(new FalseExpression));
152✔
936
    }
152✔
937
    return q;
612✔
938
}
612✔
939

940
std::unique_ptr<Subexpr> PropertyNode::visit(ParserDriver* drv, DataType)
941
{
477,802✔
942
    path->resolve_arg(drv);
477,802✔
943
    if (path->path_elems.back().is_key() && path->path_elems.back().get_key() == "@links") {
477,802✔
944
        identifier = "@links";
304✔
945
        // This is a backlink aggregate query
152✔
946
        path->path_elems.pop_back();
304✔
947
        auto link_chain = path->visit(drv, comp_type);
304✔
948
        auto sub = link_chain.get_backlink_count<Int>();
304✔
949
        return sub.clone();
304✔
950
    }
304✔
951
    m_link_chain = path->visit(drv, comp_type);
477,498✔
952
    if (!path->at_end()) {
477,498✔
953
        if (!path->current_path_elem->is_key()) {
475,670✔
954
            throw InvalidQueryError(util::format("[%1] not expected", *path->current_path_elem));
4✔
955
        }
4✔
956
        identifier = path->current_path_elem->get_key();
475,666✔
957
    }
475,666✔
958
    std::unique_ptr<Subexpr> subexpr{drv->column(m_link_chain, path)};
477,496✔
959

238,738✔
960
    Path indexes;
477,494✔
961
    while (!path->at_end()) {
481,366✔
962
        indexes.emplace_back(std::move(*(path->current_path_elem++)));
3,872✔
963
    }
3,872✔
964

238,738✔
965
    if (!indexes.empty()) {
477,494✔
966
        auto ok = false;
3,576✔
967
        const PathElement& first_index = indexes.front();
3,576✔
968
        if (indexes.size() > 1 && subexpr->get_type() != type_Mixed) {
3,576✔
969
            throw InvalidQueryError("Only Property of type 'any' can have nested collections");
×
970
        }
×
971
        if (auto mixed = dynamic_cast<Columns<Mixed>*>(subexpr.get())) {
3,576✔
972
            ok = true;
208✔
973
            mixed->path(indexes);
208✔
974
        }
208✔
975
        else if (auto dict = dynamic_cast<Columns<Dictionary>*>(subexpr.get())) {
3,368✔
976
            if (first_index.is_key()) {
224✔
977
                ok = true;
180✔
978
                auto trailing = first_index.get_key();
180✔
979
                if (trailing == "@values") {
180✔
980
                }
4✔
981
                else if (trailing == "@keys") {
176✔
982
                    subexpr = std::make_unique<ColumnDictionaryKeys>(*dict);
44✔
983
                }
44✔
984
                else {
132✔
985
                    dict->path(indexes);
132✔
986
                }
132✔
987
            }
180✔
988
            else if (first_index.is_all()) {
44✔
989
                ok = true;
36✔
990
                dict->path(indexes);
36✔
991
            }
36✔
992
        }
224✔
993
        else if (auto coll = dynamic_cast<Columns<Lst<Mixed>>*>(subexpr.get())) {
3,144✔
994
            ok = coll->indexes(indexes);
40✔
995
        }
40✔
996
        else if (auto coll = dynamic_cast<ColumnListBase*>(subexpr.get())) {
3,104✔
997
            if (indexes.size() == 1) {
3,096✔
998
                ok = coll->index(first_index);
3,096✔
999
            }
3,096✔
1000
        }
3,096✔
1001

1,788✔
1002
        if (!ok) {
3,576✔
1003
            if (first_index.is_key()) {
1,760✔
1004
                auto trailing = first_index.get_key();
1,720✔
1005
                if (!post_op && is_length_suffix(trailing)) {
1,720✔
1006
                    // If 'length' is the operator, the last id in the path must be the name
852✔
1007
                    // of a list property
852✔
1008
                    path->path_elems.pop_back();
1,704✔
1009
                    const std::string& prop = path->path_elems.back().get_key();
1,704✔
1010
                    std::unique_ptr<Subexpr> subexpr{path->visit(drv, comp_type).column(prop, false)};
1,704✔
1011
                    if (auto list = dynamic_cast<ColumnListBase*>(subexpr.get())) {
1,704✔
1012
                        if (auto length_expr = list->get_element_length())
1,704✔
1013
                            return length_expr;
1,640✔
1014
                    }
80✔
1015
                }
1,704✔
1016
                throw InvalidQueryError(util::format("Property '%1.%2' has no property '%3'",
80✔
1017
                                                     m_link_chain.get_current_table()->get_class_name(), identifier,
80✔
1018
                                                     trailing));
80✔
1019
            }
80✔
1020
            else {
40✔
1021
                throw InvalidQueryError(util::format("Property '%1.%2' does not support index '%3'",
40✔
1022
                                                     m_link_chain.get_current_table()->get_class_name(), identifier,
40✔
1023
                                                     first_index));
40✔
1024
            }
40✔
1025
        }
475,734✔
1026
    }
3,576✔
1027
    if (post_op) {
475,734✔
1028
        return post_op->visit(drv, subexpr.get());
4,124✔
1029
    }
4,124✔
1030
    return subexpr;
471,610✔
1031
}
471,610✔
1032

1033
std::unique_ptr<Subexpr> SubqueryNode::visit(ParserDriver* drv, DataType)
1034
{
304✔
1035
    if (variable_name.size() < 2 || variable_name[0] != '$') {
304✔
1036
        throw SyntaxError(util::format("The subquery variable '%1' is invalid. The variable must start with "
4✔
1037
                                       "'$' and cannot be empty; for example '$x'.",
4✔
1038
                                       variable_name));
4✔
1039
    }
4✔
1040
    LinkChain lc = prop->path->visit(drv, prop->comp_type);
300✔
1041

150✔
1042
    ColKey col_key;
300✔
1043
    std::string identifier;
300✔
1044
    if (!prop->path->at_end()) {
300✔
1045
        identifier = prop->path->next_identifier();
8✔
1046
        col_key = lc.get_current_table()->get_column_key(identifier);
8✔
1047
    }
8✔
1048
    else {
292✔
1049
        identifier = prop->path->last_identifier();
292✔
1050
        col_key = lc.get_current_col();
292✔
1051
    }
292✔
1052

150✔
1053
    auto col_type = col_key.get_type();
300✔
1054
    if (col_key.is_list() && col_type != col_type_Link) {
300✔
1055
        throw InvalidQueryError(
4✔
1056
            util::format("A subquery can not operate on a list of primitive values (property '%1')", identifier));
4✔
1057
    }
4✔
1058
    // col_key.is_list => col_type == col_type_Link
148✔
1059
    if (!(col_key.is_list() || col_type == col_type_BackLink)) {
296✔
1060
        throw InvalidQueryError(util::format("A subquery must operate on a list property, but '%1' is type '%2'",
8✔
1061
                                             identifier, realm::get_data_type_name(DataType(col_type))));
8✔
1062
    }
8✔
1063

144✔
1064
    TableRef previous_table = drv->m_base_table;
288✔
1065
    drv->m_base_table = lc.get_current_table().cast_away_const();
288✔
1066
    bool did_add = drv->m_mapping.add_mapping(drv->m_base_table, variable_name, "");
288✔
1067
    if (!did_add) {
288✔
1068
        throw InvalidQueryError(util::format("Unable to create a subquery expression with variable '%1' since an "
4✔
1069
                                             "identical variable already exists in this context",
4✔
1070
                                             variable_name));
4✔
1071
    }
4✔
1072
    Query sub = subquery->visit(drv);
284✔
1073
    drv->m_mapping.remove_mapping(drv->m_base_table, variable_name);
284✔
1074
    drv->m_base_table = previous_table;
284✔
1075

142✔
1076
    return lc.subquery(sub);
284✔
1077
}
284✔
1078

1079
std::unique_ptr<Subexpr> PostOpNode::visit(ParserDriver*, Subexpr* subexpr)
1080
{
4,124✔
1081
    if (op_type == PostOpNode::SIZE) {
4,124✔
1082
        if (auto s = dynamic_cast<Columns<Link>*>(subexpr)) {
3,484✔
1083
            return s->count().clone();
344✔
1084
        }
344✔
1085
        if (auto s = dynamic_cast<ColumnListBase*>(subexpr)) {
3,140✔
1086
            return s->size().clone();
2,928✔
1087
        }
2,928✔
1088
        if (auto s = dynamic_cast<Columns<StringData>*>(subexpr)) {
212✔
1089
            return s->size().clone();
128✔
1090
        }
128✔
1091
        if (auto s = dynamic_cast<Columns<BinaryData>*>(subexpr)) {
84✔
1092
            return s->size().clone();
48✔
1093
        }
48✔
1094
        if (auto s = dynamic_cast<Columns<Mixed>*>(subexpr)) {
36✔
1095
            return s->size().clone();
24✔
1096
        }
24✔
1097
    }
640✔
1098
    else if (op_type == PostOpNode::TYPE) {
640✔
1099
        if (auto s = dynamic_cast<Columns<Mixed>*>(subexpr)) {
640✔
1100
            return s->type_of_value().clone();
508✔
1101
        }
508✔
1102
        if (auto s = dynamic_cast<ColumnsCollection<Mixed>*>(subexpr)) {
132✔
1103
            return s->type_of_value().clone();
112✔
1104
        }
112✔
1105
        if (auto s = dynamic_cast<ObjPropertyBase*>(subexpr)) {
20✔
1106
            return Value<TypeOfValue>(TypeOfValue(s->column_key())).clone();
8✔
1107
        }
8✔
1108
        if (dynamic_cast<Columns<Link>*>(subexpr)) {
12✔
1109
            return Value<TypeOfValue>(TypeOfValue(TypeOfValue::Attribute::ObjectLink)).clone();
12✔
1110
        }
12✔
1111
    }
12✔
1112

6✔
1113
    if (subexpr) {
12✔
1114
        throw InvalidQueryError(util::format("Operation '%1' is not supported on property of type '%2'", op_name,
12✔
1115
                                             get_data_type_name(DataType(subexpr->get_type()))));
12✔
1116
    }
12✔
1117
    REALM_UNREACHABLE();
UNCOV
1118
    return {};
×
UNCOV
1119
}
×
1120

1121
std::unique_ptr<Subexpr> LinkAggrNode::visit(ParserDriver* drv, DataType)
1122
{
848✔
1123
    auto subexpr = property->visit(drv);
848✔
1124
    auto link_prop = dynamic_cast<Columns<Link>*>(subexpr.get());
848✔
1125
    if (!link_prop) {
848✔
1126
        throw InvalidQueryError(util::format("Operation '%1' cannot apply to property '%2' because it is not a list",
40✔
1127
                                             agg_op_type_to_str(type), property->get_identifier()));
40✔
1128
    }
40✔
1129
    const LinkChain& link_chain = property->link_chain();
808✔
1130
    prop_name = drv->translate(link_chain, prop_name);
808✔
1131
    auto col_key = link_chain.get_current_table()->get_column_key(prop_name);
808✔
1132

404✔
1133
    switch (col_key.get_type()) {
808✔
1134
        case col_type_Int:
64✔
1135
            subexpr = link_prop->column<Int>(col_key).clone();
64✔
1136
            break;
64✔
1137
        case col_type_Float:
128✔
1138
            subexpr = link_prop->column<float>(col_key).clone();
128✔
1139
            break;
128✔
1140
        case col_type_Double:
344✔
1141
            subexpr = link_prop->column<double>(col_key).clone();
344✔
1142
            break;
344✔
1143
        case col_type_Decimal:
96✔
1144
            subexpr = link_prop->column<Decimal>(col_key).clone();
96✔
1145
            break;
96✔
1146
        case col_type_Timestamp:
56✔
1147
            subexpr = link_prop->column<Timestamp>(col_key).clone();
56✔
1148
            break;
56✔
1149
        case col_type_Mixed:
64✔
1150
            subexpr = link_prop->column<Mixed>(col_key).clone();
64✔
1151
            break;
64✔
1152
        default:
48✔
1153
            throw InvalidQueryError(util::format("collection aggregate not supported for type '%1'",
48✔
1154
                                                 get_data_type_name(DataType(col_key.get_type()))));
48✔
1155
    }
752✔
1156
    return aggregate(subexpr.get());
752✔
1157
}
752✔
1158

1159
std::unique_ptr<Subexpr> ListAggrNode::visit(ParserDriver* drv, DataType)
1160
{
2,744✔
1161
    auto subexpr = property->visit(drv);
2,744✔
1162
    return aggregate(subexpr.get());
2,744✔
1163
}
2,744✔
1164

1165
std::unique_ptr<Subexpr> AggrNode::aggregate(Subexpr* subexpr)
1166
{
3,496✔
1167
    std::unique_ptr<Subexpr> agg;
3,496✔
1168
    if (auto list_prop = dynamic_cast<ColumnListBase*>(subexpr)) {
3,496✔
1169
        switch (type) {
2,712✔
1170
            case MAX:
632✔
1171
                agg = list_prop->max_of();
632✔
1172
                break;
632✔
1173
            case MIN:
608✔
1174
                agg = list_prop->min_of();
608✔
1175
                break;
608✔
1176
            case SUM:
800✔
1177
                agg = list_prop->sum_of();
800✔
1178
                break;
800✔
1179
            case AVG:
672✔
1180
                agg = list_prop->avg_of();
672✔
1181
                break;
672✔
1182
        }
784✔
1183
    }
784✔
1184
    else if (auto prop = dynamic_cast<SubColumnBase*>(subexpr)) {
784✔
1185
        switch (type) {
752✔
1186
            case MAX:
216✔
1187
                agg = prop->max_of();
216✔
1188
                break;
216✔
1189
            case MIN:
208✔
1190
                agg = prop->min_of();
208✔
1191
                break;
208✔
1192
            case SUM:
148✔
1193
                agg = prop->sum_of();
148✔
1194
                break;
148✔
1195
            case AVG:
180✔
1196
                agg = prop->avg_of();
180✔
1197
                break;
180✔
1198
        }
3,496✔
1199
    }
3,496✔
1200
    if (!agg) {
3,496✔
1201
        throw InvalidQueryError(
232✔
1202
            util::format("Cannot use aggregate '%1' for this type of property", agg_op_type_to_str(type)));
232✔
1203
    }
232✔
1204

1,632✔
1205
    return agg;
3,264✔
1206
}
3,264✔
1207

1208
void ConstantNode::decode_b64()
1209
{
1,432✔
1210
    const size_t encoded_size = text.size() - 5;
1,432✔
1211
    size_t buffer_size = util::base64_decoded_size(encoded_size);
1,432✔
1212
    m_decode_buffer.resize(buffer_size);
1,432✔
1213
    StringData window(text.c_str() + 4, encoded_size);
1,432✔
1214
    util::Optional<size_t> decoded_size = util::base64_decode(window, m_decode_buffer);
1,432✔
1215
    if (!decoded_size) {
1,432✔
1216
        throw SyntaxError("Invalid base64 value");
×
1217
    }
×
1218
    REALM_ASSERT_DEBUG_EX(*decoded_size <= encoded_size, *decoded_size, encoded_size);
1,432✔
1219
    m_decode_buffer.resize(*decoded_size); // truncate
1,432✔
1220
}
1,432✔
1221

1222
Mixed ConstantNode::get_value()
1223
{
41,778✔
1224
    switch (type) {
41,778✔
1225
        case Type::NUMBER:
24,056✔
1226
            return int64_t(strtoll(text.c_str(), nullptr, 0));
24,056✔
1227
        case Type::FLOAT:
2,136✔
1228
            if (text[text.size() - 1] == 'f') {
2,136✔
1229
                return strtof(text.c_str(), nullptr);
4✔
1230
            }
4✔
1231
            return strtod(text.c_str(), nullptr);
2,132✔
1232
        case Type::INFINITY_VAL: {
1,142✔
1233
            bool negative = text[0] == '-';
152✔
1234
            constexpr auto inf = std::numeric_limits<double>::infinity();
152✔
1235
            return negative ? -inf : inf;
124✔
1236
        }
2,132✔
1237
        case Type::NAN_VAL:
1,098✔
1238
            return type_punning<double>(0x7ff8000000000000);
64✔
1239
        case Type::STRING:
8,032✔
1240
            return StringData(text.data() + 1, text.size() - 2);
8,032✔
1241
        case Type::STRING_BASE64:
1,424✔
1242
            decode_b64();
716✔
1243
            return StringData(m_decode_buffer.data(), m_decode_buffer.size());
716✔
1244
        case Type::TIMESTAMP: {
1,372✔
1245
            auto s = text;
612✔
1246
            int64_t seconds;
612✔
1247
            int32_t nanoseconds;
612✔
1248
            if (s[0] == 'T') {
612✔
1249
                size_t colon_pos = s.find(":");
536✔
1250
                std::string s1 = s.substr(1, colon_pos - 1);
536✔
1251
                std::string s2 = s.substr(colon_pos + 1);
536✔
1252
                seconds = strtol(s1.c_str(), nullptr, 0);
536✔
1253
                nanoseconds = int32_t(strtol(s2.c_str(), nullptr, 0));
536✔
1254
            }
536✔
1255
            else {
76✔
1256
                // readable format YYYY-MM-DD-HH:MM:SS:NANOS nanos optional
38✔
1257
                struct tm tmp = tm();
76✔
1258
                char sep = s.find("@") < s.size() ? '@' : 'T';
62✔
1259
                std::string fmt = "%d-%d-%d"s + sep + "%d:%d:%d:%d"s;
76✔
1260
                int cnt = sscanf(s.c_str(), fmt.c_str(), &tmp.tm_year, &tmp.tm_mon, &tmp.tm_mday, &tmp.tm_hour,
76✔
1261
                                 &tmp.tm_min, &tmp.tm_sec, &nanoseconds);
76✔
1262
                REALM_ASSERT(cnt >= 6);
76✔
1263
                tmp.tm_year -= 1900; // epoch offset (see man mktime)
76✔
1264
                tmp.tm_mon -= 1;     // converts from 1-12 to 0-11
76✔
1265

38✔
1266
                if (tmp.tm_year < 0) {
76✔
1267
                    // platform timegm functions do not throw errors, they return -1 which is also a valid time
8✔
1268
                    throw InvalidQueryError("Conversion of dates before 1900 is not supported.");
16✔
1269
                }
16✔
1270

30✔
1271
                seconds = platform_timegm(tmp); // UTC time
60✔
1272
                if (cnt == 6) {
60✔
1273
                    nanoseconds = 0;
32✔
1274
                }
32✔
1275
                if (nanoseconds < 0) {
60✔
1276
                    throw SyntaxError("The nanoseconds of a Timestamp cannot be negative.");
×
1277
                }
×
1278
                if (seconds < 0) { // seconds determines the sign of the nanoseconds part
60✔
1279
                    nanoseconds *= -1;
24✔
1280
                }
24✔
1281
            }
60✔
1282
            return get_timestamp_if_valid(seconds, nanoseconds);
604✔
1283
        }
612✔
1284
        case Type::UUID_T:
672✔
1285
            return UUID(text.substr(5, text.size() - 6));
672✔
1286
        case Type::OID:
772✔
1287
            return ObjectId(text.substr(4, text.size() - 5).c_str());
772✔
1288
        case Type::LINK:
310✔
1289
            return ObjKey(strtol(text.substr(1, text.size() - 1).c_str(), nullptr, 0));
8✔
1290
        case Type::TYPED_LINK: {
330✔
1291
            size_t colon_pos = text.find(":");
48✔
1292
            auto table_key_val = uint32_t(strtol(text.substr(1, colon_pos - 1).c_str(), nullptr, 0));
48✔
1293
            auto obj_key_val = strtol(text.substr(colon_pos + 1).c_str(), nullptr, 0);
48✔
1294
            return ObjLink(TableKey(table_key_val), ObjKey(obj_key_val));
48✔
1295
        }
612✔
1296
        case Type::NULL_VAL:
2,252✔
1297
            return {};
2,252✔
1298
        case Type::TRUE:
386✔
1299
            return {true};
160✔
1300
        case Type::FALSE:
462✔
1301
            return {false};
312✔
1302
        case Type::ARG:
306✔
1303
            break;
×
1304
        case BINARY_STR: {
1,068✔
1305
            return BinaryData(text.data() + 1, text.size() - 2);
1,068✔
1306
        }
612✔
1307
        case BINARY_BASE64:
716✔
1308
            decode_b64();
716✔
1309
            return BinaryData(m_decode_buffer.data(), m_decode_buffer.size());
716✔
1310
    }
×
1311
    return {};
×
1312
}
×
1313

1314
std::unique_ptr<Subexpr> ConstantNode::visit(ParserDriver* drv, DataType hint)
1315
{
474,850✔
1316
    std::unique_ptr<Subexpr> ret;
474,850✔
1317
    std::string explain_value_message = text;
474,850✔
1318
    Mixed value;
474,850✔
1319

237,404✔
1320
    if (type == Type::ARG) {
474,850✔
1321
        size_t arg_no = size_t(strtol(text.substr(1).c_str(), nullptr, 10));
433,076✔
1322
        if (m_comp_type && !drv->m_args.is_argument_list(arg_no)) {
433,076✔
1323
            throw InvalidQueryError(util::format(
12✔
1324
                "ANY/ALL/NONE are only allowed on arguments which contain a list but '%1' is not a list.",
12✔
1325
                explain_value_message));
12✔
1326
        }
12✔
1327
        if (drv->m_args.is_argument_list(arg_no)) {
433,064✔
1328
            std::vector<Mixed> mixed_list = drv->m_args.list_for_argument(arg_no);
144✔
1329
            return copy_list_of_args(mixed_list);
144✔
1330
        }
144✔
1331
        if (drv->m_args.is_argument_null(arg_no)) {
432,920✔
1332
            explain_value_message = util::format("argument '%1' which is NULL", explain_value_message);
256✔
1333
        }
256✔
1334
        else {
432,664✔
1335
            value = drv->m_args.mixed_for_argument(arg_no);
432,664✔
1336
            if (value.is_null()) {
432,664✔
1337
                explain_value_message = util::format("argument %1 of type null", explain_value_message);
4✔
1338
            }
4✔
1339
            else if (value.is_type(type_TypedLink)) {
432,660✔
1340
                explain_value_message =
36✔
1341
                    util::format("%1 which links to %2", explain_value_message,
36✔
1342
                                 print_pretty_objlink(value.get<ObjLink>(), drv->m_base_table->get_parent_group()));
36✔
1343
            }
36✔
1344
            else {
432,624✔
1345
                explain_value_message = util::format("argument %1 with value '%2'", explain_value_message, value);
432,624✔
1346
                if (!(m_target_table || Mixed::data_types_are_comparable(value.get_type(), hint) ||
432,624✔
1347
                      Mixed::is_numeric(hint) || (value.is_type(type_String) && hint == type_TypeOfValue))) {
216,402✔
1348
                    throw InvalidQueryArgError(
124✔
1349
                        util::format("Cannot compare %1 to a %2", explain_value_message, get_data_type_name(hint)));
124✔
1350
                }
124✔
1351
            }
41,774✔
1352
        }
432,664✔
1353
    }
41,774✔
1354
    else {
41,774✔
1355
        value = get_value();
41,774✔
1356
    }
41,774✔
1357

237,404✔
1358
    if (m_target_table) {
474,710✔
1359
        // There is a table name set. This must be an ObjLink
56✔
1360
        const Group* g = drv->m_base_table->get_parent_group();
112✔
1361
        auto table = g->get_table(m_target_table);
112✔
1362
        if (!table) {
112✔
1363
            // Perhaps class prefix is missing
4✔
1364
            Group::TableNameBuffer buffer;
8✔
1365
            table = g->get_table(Group::class_name_to_table_name(m_target_table, buffer));
8✔
1366
        }
8✔
1367
        if (!table) {
112✔
1368
            throw InvalidQueryError(util::format("Unknown object type '%1'", m_target_table));
×
1369
        }
×
1370
        auto obj_key = table->find_primary_key(value);
112✔
1371
        value = ObjLink(table->get_key(), ObjKey(obj_key));
112✔
1372
    }
112✔
1373

237,264✔
1374
    if (value.is_null()) {
474,570✔
1375
        if (hint == type_String) {
2,512✔
1376
            return std::make_unique<ConstantStringValue>(StringData()); // Null string
332✔
1377
        }
332✔
1378
        else if (hint == type_Binary) {
2,180✔
1379
            return std::make_unique<Value<Binary>>(BinaryData()); // Null string
320✔
1380
        }
320✔
1381
        else {
1,860✔
1382
            return std::make_unique<Value<null>>(realm::null());
1,860✔
1383
        }
1,860✔
1384
    }
472,058✔
1385

236,008✔
1386
    switch (value.get_type()) {
472,058✔
1387
        case type_Int: {
24,864✔
1388
            if (hint == type_Decimal) {
24,864✔
1389
                ret = std::make_unique<Value<Decimal128>>(Decimal128(value.get_int()));
1,296✔
1390
            }
1,296✔
1391
            else {
23,568✔
1392
                ret = std::make_unique<Value<int64_t>>(value.get_int());
23,568✔
1393
            }
23,568✔
1394
            break;
24,864✔
1395
        }
×
1396
        case type_Float: {
424✔
1397
            ret = std::make_unique<Value<float>>(value.get_float());
424✔
1398
            break;
424✔
1399
        }
×
1400
        case type_Decimal:
488✔
1401
            ret = std::make_unique<Value<Decimal128>>(value.get_decimal());
488✔
1402
            break;
488✔
1403
        case type_Double: {
2,860✔
1404
            auto double_val = value.get_double();
2,860✔
1405
            if (std::isinf(double_val) && (!Mixed::is_numeric(hint) || hint == type_Int)) {
2,860✔
1406
                throw InvalidQueryError(util::format("Infinity not supported for %1", get_data_type_name(hint)));
8✔
1407
            }
8✔
1408

1,426✔
1409
            switch (hint) {
2,852✔
1410
                case type_Float:
184✔
1411
                    ret = std::make_unique<Value<float>>(float(double_val));
184✔
1412
                    break;
184✔
1413
                case type_Decimal: {
480✔
1414
                    // If not argument, try decode again to get full precision
240✔
1415
                    Decimal128 dec = (type == Type::ARG) ? Decimal128(double_val) : Decimal128(text);
478✔
1416
                    ret = std::make_unique<Value<Decimal128>>(dec);
480✔
1417
                    break;
480✔
1418
                }
×
1419
                case type_Int: {
28✔
1420
                    int64_t int_val = int64_t(double_val);
28✔
1421
                    // Only return an integer if it precisely represents val
14✔
1422
                    if (double(int_val) == double_val) {
28✔
1423
                        ret = std::make_unique<Value<int64_t>>(int_val);
8✔
1424
                        break;
8✔
1425
                    }
8✔
1426
                    [[fallthrough]];
20✔
1427
                }
20✔
1428
                default:
2,180✔
1429
                    ret = std::make_unique<Value<double>>(double_val);
2,180✔
1430
                    break;
2,180✔
1431
            }
2,852✔
1432
            break;
2,852✔
1433
        }
2,852✔
1434
        case type_String: {
9,040✔
1435
            StringData str = value.get_string();
9,040✔
1436
            switch (hint) {
9,040✔
1437
                case type_Int:
416✔
1438
                    ret = std::make_unique<Value<int64_t>>(string_to<int64_t>(str));
416✔
1439
                    break;
416✔
1440
                case type_Float:
8✔
1441
                    ret = std::make_unique<Value<float>>(string_to<float>(str));
8✔
1442
                    break;
8✔
1443
                case type_Double:
8✔
1444
                    ret = std::make_unique<Value<double>>(string_to<double>(str));
8✔
1445
                    break;
8✔
1446
                case type_Decimal:
4✔
1447
                    ret = std::make_unique<Value<Decimal128>>(string_to<Decimal128>(str));
4✔
1448
                    break;
4✔
1449
                default:
8,604✔
1450
                    if (hint == type_TypeOfValue) {
8,604✔
1451
                        TypeOfValue type_of_value(std::string_view(str.data(), str.size()));
552✔
1452
                        ret = std::make_unique<Value<TypeOfValue>>(type_of_value);
552✔
1453
                    }
552✔
1454
                    else {
8,052✔
1455
                        ret = std::make_unique<ConstantStringValue>(str);
8,052✔
1456
                    }
8,052✔
1457
                    break;
8,604✔
1458
            }
8,984✔
1459
            break;
8,984✔
1460
        }
8,984✔
1461
        case type_Timestamp:
4,972✔
1462
            ret = std::make_unique<Value<Timestamp>>(value.get_timestamp());
960✔
1463
            break;
960✔
1464
        case type_UUID:
5,024✔
1465
            ret = std::make_unique<Value<UUID>>(value.get_uuid());
1,064✔
1466
            break;
1,064✔
1467
        case type_ObjectId:
429,376✔
1468
            ret = std::make_unique<Value<ObjectId>>(value.get_object_id());
429,376✔
1469
            break;
429,376✔
1470
        case type_Link:
4,502✔
1471
            ret = std::make_unique<Value<ObjKey>>(value.get<ObjKey>());
20✔
1472
            break;
20✔
1473
        case type_TypedLink:
4,590✔
1474
            ret = std::make_unique<Value<ObjLink>>(value.get<ObjLink>());
196✔
1475
            break;
196✔
1476
        case type_Bool:
4,880✔
1477
            ret = std::make_unique<Value<Bool>>(value.get_bool());
776✔
1478
            break;
776✔
1479
        case type_Binary:
5,466✔
1480
            ret = std::make_unique<ConstantBinaryValue>(value.get_binary());
1,948✔
1481
            break;
1,948✔
1482
        case type_Mixed:
4,492✔
1483
            break;
×
1484
    }
471,984✔
1485
    if (!ret) {
471,984✔
1486
        throw InvalidQueryError(
×
1487
            util::format("Unsupported comparison between property of type '%1' and constant value: %2",
×
1488
                         get_data_type_name(hint), explain_value_message));
×
1489
    }
×
1490
    return ret;
471,984✔
1491
}
471,984✔
1492

1493
std::unique_ptr<ConstantMixedList> ConstantNode::copy_list_of_args(std::vector<Mixed>& mixed_args)
1494
{
144✔
1495
    std::unique_ptr<ConstantMixedList> args_in_list = std::make_unique<ConstantMixedList>(mixed_args.size());
144✔
1496
    size_t ndx = 0;
144✔
1497
    for (const auto& mixed : mixed_args) {
352✔
1498
        args_in_list->set(ndx++, mixed);
352✔
1499
    }
352✔
1500
    if (m_comp_type) {
144✔
1501
        args_in_list->set_comparison_type(*m_comp_type);
20✔
1502
    }
20✔
1503
    return args_in_list;
144✔
1504
}
144✔
1505

1506
Mixed Arguments::mixed_for_argument(size_t arg_no)
1507
{
3,988✔
1508
    switch (type_for_argument(arg_no)) {
3,988✔
1509
        case type_Int:
464✔
1510
            return int64_t(long_for_argument(arg_no));
464✔
1511
        case type_String:
380✔
1512
            return string_for_argument(arg_no);
380✔
1513
        case type_Binary:
176✔
1514
            return binary_for_argument(arg_no);
176✔
1515
        case type_Bool:
316✔
1516
            return bool_for_argument(arg_no);
316✔
1517
        case type_Float:
436✔
1518
            return float_for_argument(arg_no);
436✔
1519
        case type_Double:
440✔
1520
            return double_for_argument(arg_no);
440✔
1521
        case type_Timestamp:
392✔
1522
            try {
392✔
1523
                return timestamp_for_argument(arg_no);
392✔
1524
            }
392✔
1525
            catch (const std::exception&) {
×
1526
            }
×
1527
            return objectid_for_argument(arg_no);
196✔
1528
        case type_ObjectId:
524✔
1529
            try {
524✔
1530
                return objectid_for_argument(arg_no);
524✔
1531
            }
524✔
1532
            catch (const std::exception&) {
×
1533
            }
×
1534
            return timestamp_for_argument(arg_no);
262✔
1535
        case type_Decimal:
472✔
1536
            return decimal128_for_argument(arg_no);
420✔
1537
        case type_UUID:
456✔
1538
            return uuid_for_argument(arg_no);
388✔
1539
        case type_Link:
268✔
1540
            return object_index_for_argument(arg_no);
12✔
1541
        case type_TypedLink:
282✔
1542
            return objlink_for_argument(arg_no);
40✔
1543
        default:
262✔
1544
            break;
×
1545
    }
×
1546
    return {};
×
1547
}
×
1548

1549
#if REALM_ENABLE_GEOSPATIAL
1550
GeospatialNode::GeospatialNode(GeospatialNode::Box, GeoPoint& p1, GeoPoint& p2)
1551
    : m_geo{Geospatial{GeoBox{p1, p2}}}
1552
{
20✔
1553
}
20✔
1554

1555
GeospatialNode::GeospatialNode(Circle, GeoPoint& p, double radius)
1556
    : m_geo{Geospatial{GeoCircle{radius, p}}}
1557
{
60✔
1558
}
60✔
1559

1560
GeospatialNode::GeospatialNode(Polygon, GeoPoint& p)
1561
    : m_points({{p}})
1562
{
×
1563
}
×
1564

1565
GeospatialNode::GeospatialNode(Loop, GeoPoint& p)
1566
    : m_points({{p}})
1567
{
68✔
1568
}
68✔
1569

1570
void GeospatialNode::add_point_to_loop(GeoPoint& p)
1571
{
272✔
1572
    m_points.back().push_back(p);
272✔
1573
}
272✔
1574

1575
void GeospatialNode::add_loop_to_polygon(GeospatialNode* node)
1576
{
8✔
1577
    m_points.push_back(node->m_points.back());
8✔
1578
}
8✔
1579

1580
std::unique_ptr<Subexpr> GeospatialNode::visit(ParserDriver*, DataType)
1581
{
124✔
1582
    std::unique_ptr<Subexpr> ret;
124✔
1583
    if (m_geo.get_type() != Geospatial::Type::Invalid) {
124✔
1584
        ret = std::make_unique<ConstantGeospatialValue>(m_geo);
72✔
1585
    }
72✔
1586
    else {
52✔
1587
        ret = std::make_unique<ConstantGeospatialValue>(GeoPolygon{m_points});
52✔
1588
    }
52✔
1589
    return ret;
124✔
1590
}
124✔
1591
#endif
1592

1593
std::unique_ptr<Subexpr> ListNode::visit(ParserDriver* drv, DataType hint)
1594
{
2,364✔
1595
    if (hint == type_TypeOfValue) {
2,364✔
1596
        try {
12✔
1597
            std::unique_ptr<Value<TypeOfValue>> ret = std::make_unique<Value<TypeOfValue>>();
12✔
1598
            constexpr bool is_list = true;
12✔
1599
            ret->init(is_list, elements.size());
12✔
1600
            ret->set_comparison_type(m_comp_type);
12✔
1601
            size_t ndx = 0;
12✔
1602
            for (auto constant : elements) {
24✔
1603
                std::unique_ptr<Subexpr> evaluated = constant->visit(drv, hint);
24✔
1604
                if (auto converted = dynamic_cast<Value<TypeOfValue>*>(evaluated.get())) {
24✔
1605
                    ret->set(ndx++, converted->get(0));
20✔
1606
                }
20✔
1607
                else {
4✔
1608
                    throw InvalidQueryError(util::format("Invalid constant inside constant list: %1",
4✔
1609
                                                         evaluated->description(drv->m_serializer_state)));
4✔
1610
                }
4✔
1611
            }
24✔
1612
            return ret;
10✔
1613
        }
×
1614
        catch (const std::runtime_error& e) {
×
1615
            throw InvalidQueryArgError(e.what());
×
1616
        }
×
1617
    }
2,352✔
1618

1,176✔
1619
    auto ret = std::make_unique<ConstantMixedList>(elements.size());
2,352✔
1620
    ret->set_comparison_type(m_comp_type);
2,352✔
1621
    size_t ndx = 0;
2,352✔
1622
    for (auto constant : elements) {
4,896✔
1623
        auto evaulated_constant = constant->visit(drv, hint);
4,896✔
1624
        if (auto value = dynamic_cast<const ValueBase*>(evaulated_constant.get())) {
4,896✔
1625
            REALM_ASSERT_EX(value->size() == 1, value->size());
4,896✔
1626
            ret->set(ndx++, value->get(0));
4,896✔
1627
        }
4,896✔
1628
        else {
×
1629
            throw InvalidQueryError("Invalid constant inside constant list");
×
1630
        }
×
1631
    }
4,896✔
1632
    return ret;
2,352✔
1633
}
2,352✔
1634

1635
void PathNode::resolve_arg(ParserDriver* drv)
1636
{
477,804✔
1637
    if (arg.size()) {
477,804✔
1638
        if (path_elems.size()) {
32✔
1639
            throw InvalidQueryError("Key path argument cannot be mixed with other elements");
×
1640
        }
×
1641
        auto arg_str = drv->get_arg_for_key_path(arg);
32✔
1642
        const char* path = arg_str.data();
32✔
1643
        do {
44✔
1644
            auto p = find_chr(path, '.');
44✔
1645
            StringData elem(path, p - path);
44✔
1646
            add_element(elem);
44✔
1647
            path = p;
44✔
1648
        } while (*path++ == '.');
44✔
1649
    }
32✔
1650
}
477,804✔
1651

1652
LinkChain PathNode::visit(ParserDriver* drv, util::Optional<ExpressionComparisonType> comp_type)
1653
{
479,768✔
1654
    LinkChain link_chain(drv->m_base_table, comp_type);
479,768✔
1655
    for (current_path_elem = path_elems.begin(); current_path_elem != path_elems.end(); ++current_path_elem) {
496,740✔
1656
        if (current_path_elem->is_key()) {
494,362✔
1657
            const std::string& raw_path_elem = current_path_elem->get_key();
494,292✔
1658
            auto path_elem = drv->translate(link_chain, raw_path_elem);
494,292✔
1659
            if (path_elem.find("@links.") == 0) {
494,292✔
1660
                std::string_view table_column_pair(path_elem);
504✔
1661
                table_column_pair = table_column_pair.substr(7);
504✔
1662
                auto dot_pos = table_column_pair.find('.');
504✔
1663
                auto table_name = table_column_pair.substr(0, dot_pos);
504✔
1664
                auto column_name = table_column_pair.substr(dot_pos + 1);
504✔
1665
                drv->backlink(link_chain, table_name, column_name);
504✔
1666
                continue;
504✔
1667
            }
504✔
1668
            if (path_elem == "@values") {
493,788✔
1669
                if (!link_chain.get_current_col().is_dictionary()) {
24✔
1670
                    throw InvalidQueryError("@values only allowed on dictionaries");
×
1671
                }
×
1672
                continue;
24✔
1673
            }
24✔
1674
            if (path_elem.empty()) {
493,764✔
1675
                continue; // this element has been removed, this happens in subqueries
284✔
1676
            }
284✔
1677

246,734✔
1678
            // Check if it is a link
246,734✔
1679
            if (link_chain.link(path_elem)) {
493,480✔
1680
                continue;
16,060✔
1681
            }
16,060✔
1682
            // The next identifier being a property on the linked to object takes precedence
238,704✔
1683
            if (link_chain.get_current_table()->get_column_key(path_elem)) {
477,420✔
1684
                break;
477,258✔
1685
            }
477,258✔
1686
        }
232✔
1687
        if (!link_chain.index(*current_path_elem))
232✔
1688
            break;
132✔
1689
    }
232✔
1690
    return link_chain;
479,768✔
1691
}
479,768✔
1692

1693
DescriptorNode::~DescriptorNode() {}
1,404✔
1694

1695
DescriptorOrderingNode::~DescriptorOrderingNode() {}
45,464✔
1696

1697
std::unique_ptr<DescriptorOrdering> DescriptorOrderingNode::visit(ParserDriver* drv)
1698
{
43,044✔
1699
    auto target = drv->m_base_table;
43,044✔
1700
    std::unique_ptr<DescriptorOrdering> ordering;
43,044✔
1701
    for (auto cur_ordering : orderings) {
22,076✔
1702
        if (!ordering)
1,140✔
1703
            ordering = std::make_unique<DescriptorOrdering>();
708✔
1704
        if (cur_ordering->get_type() == DescriptorNode::LIMIT) {
1,140✔
1705
            ordering->append_limit(LimitDescriptor(cur_ordering->limit));
340✔
1706
        }
340✔
1707
        else {
800✔
1708
            bool is_distinct = cur_ordering->get_type() == DescriptorNode::DISTINCT;
800✔
1709
            std::vector<std::vector<ExtendedColumnKey>> property_columns;
800✔
1710
            for (Path& path : cur_ordering->columns) {
836✔
1711
                std::vector<ExtendedColumnKey> columns;
836✔
1712
                LinkChain link_chain(target);
836✔
1713
                ColKey col_key;
836✔
1714
                for (size_t ndx_in_path = 0; ndx_in_path < path.size(); ++ndx_in_path) {
1,780✔
1715
                    std::string prop_name = drv->translate(link_chain, path[ndx_in_path].get_key());
952✔
1716
                    // If last column was a dictionary, We will treat the next entry as a key to
476✔
1717
                    // the dictionary
476✔
1718
                    if (col_key && col_key.is_dictionary()) {
952✔
1719
                        columns.back().set_index(prop_name);
32✔
1720
                    }
32✔
1721
                    else {
920✔
1722
                        col_key = link_chain.get_current_table()->get_column_key(prop_name);
920✔
1723
                        if (!col_key) {
920✔
1724
                            throw InvalidQueryError(util::format(
8✔
1725
                                "No property '%1' found on object type '%2' specified in '%3' clause", prop_name,
8✔
1726
                                link_chain.get_current_table()->get_class_name(), is_distinct ? "distinct" : "sort"));
6✔
1727
                        }
8✔
1728
                        columns.emplace_back(col_key);
912✔
1729
                        if (ndx_in_path < path.size() - 1) {
912✔
1730
                            link_chain.link(col_key);
116✔
1731
                        }
116✔
1732
                    }
912✔
1733
                }
952✔
1734
                property_columns.push_back(columns);
832✔
1735
            }
828✔
1736

400✔
1737
            if (is_distinct) {
796✔
1738
                ordering->append_distinct(DistinctDescriptor(property_columns));
252✔
1739
            }
252✔
1740
            else {
540✔
1741
                ordering->append_sort(SortDescriptor(property_columns, cur_ordering->ascending),
540✔
1742
                                      SortDescriptor::MergeMode::prepend);
540✔
1743
            }
540✔
1744
        }
792✔
1745
    }
1,140✔
1746

21,506✔
1747
    return ordering;
43,040✔
1748
}
43,044✔
1749

1750
// If one of the expresions is constant, it should be right
1751
static void verify_conditions(Subexpr* left, Subexpr* right, util::serializer::SerialisationState& state)
1752
{
473,946✔
1753
    if (dynamic_cast<ColumnListBase*>(left) && dynamic_cast<ColumnListBase*>(right)) {
473,946✔
1754
        throw InvalidQueryError(
32✔
1755
            util::format("Ordered comparison between two primitive lists is not implemented yet ('%1' and '%2')",
32✔
1756
                         left->description(state), right->description(state)));
32✔
1757
    }
32✔
1758
    if (dynamic_cast<Value<TypeOfValue>*>(left) && dynamic_cast<Value<TypeOfValue>*>(right)) {
473,914✔
1759
        throw InvalidQueryError(util::format("Comparison between two constants is not supported ('%1' and '%2')",
8✔
1760
                                             left->description(state), right->description(state)));
8✔
1761
    }
8✔
1762
    if (auto link_column = dynamic_cast<Columns<Link>*>(left)) {
473,906✔
1763
        if (link_column->has_multiple_values() && right->has_single_value() && right->get_mixed().is_null()) {
428✔
1764
            throw InvalidQueryError(
20✔
1765
                util::format("Cannot compare linklist ('%1') with NULL", left->description(state)));
20✔
1766
        }
20✔
1767
    }
428✔
1768
}
473,906✔
1769

1770
ParserDriver::ParserDriver(TableRef t, Arguments& args, const query_parser::KeyPathMapping& mapping)
1771
    : m_base_table(t)
1772
    , m_args(args)
1773
    , m_mapping(mapping)
1774
{
45,928✔
1775
    yylex_init(&m_yyscanner);
45,928✔
1776
}
45,928✔
1777

1778
ParserDriver::~ParserDriver()
1779
{
45,914✔
1780
    yylex_destroy(m_yyscanner);
45,914✔
1781
}
45,914✔
1782

1783
PathElement ParserDriver::get_arg_for_index(const std::string& i)
1784
{
4✔
1785
    REALM_ASSERT(i[0] == '$');
4✔
1786
    size_t arg_no = size_t(strtol(i.substr(1).c_str(), nullptr, 10));
4✔
1787
    if (m_args.is_argument_null(arg_no) || m_args.is_argument_list(arg_no)) {
4✔
1788
        throw InvalidQueryError("Invalid index parameter");
×
1789
    }
×
1790
    auto type = m_args.type_for_argument(arg_no);
4✔
1791
    switch (type) {
4✔
1792
        case type_Int:
✔
1793
            return size_t(m_args.long_for_argument(arg_no));
×
1794
        case type_String:
4✔
1795
            return m_args.string_for_argument(arg_no);
4✔
1796
        default:
✔
1797
            throw InvalidQueryError("Invalid index type");
×
1798
    }
4✔
1799
}
4✔
1800

1801
std::string ParserDriver::get_arg_for_key_path(const std::string& i)
1802
{
32✔
1803
    REALM_ASSERT(i[0] == '$');
32✔
1804
    REALM_ASSERT(i[1] == 'K');
32✔
1805
    size_t arg_no = size_t(strtol(i.substr(2).c_str(), nullptr, 10));
32✔
1806
    if (m_args.is_argument_null(arg_no) || m_args.is_argument_list(arg_no)) {
32✔
1807
        throw InvalidQueryArgError(util::format("Null or list cannot be used for parameter '%1'", i));
4✔
1808
    }
4✔
1809
    auto type = m_args.type_for_argument(arg_no);
28✔
1810
    if (type != type_String) {
28✔
1811
        throw InvalidQueryArgError(util::format("Invalid index type for '%1'. Expected a string, but found type '%2'",
4✔
1812
                                                i, get_data_type_name(type)));
4✔
1813
    }
4✔
1814
    return m_args.string_for_argument(arg_no);
24✔
1815
}
24✔
1816

1817
double ParserDriver::get_arg_for_coordinate(const std::string& str)
1818
{
80✔
1819
    REALM_ASSERT(str[0] == '$');
80✔
1820
    size_t arg_no = size_t(strtol(str.substr(1).c_str(), nullptr, 10));
80✔
1821
    if (m_args.is_argument_null(arg_no)) {
80✔
1822
        throw InvalidQueryError(util::format("NULL cannot be used in coordinate at argument '%1'", str));
4✔
1823
    }
4✔
1824
    if (m_args.is_argument_list(arg_no)) {
76✔
1825
        throw InvalidQueryError(util::format("A list cannot be used in a coordinate at argument '%1'", str));
×
1826
    }
×
1827

38✔
1828
    auto type = m_args.type_for_argument(arg_no);
76✔
1829
    switch (type) {
76✔
1830
        case type_Int:
✔
1831
            return double(m_args.long_for_argument(arg_no));
×
1832
        case type_Double:
68✔
1833
            return m_args.double_for_argument(arg_no);
68✔
1834
        case type_Float:
✔
1835
            return double(m_args.float_for_argument(arg_no));
×
1836
        default:
8✔
1837
            throw InvalidQueryError(util::format("Invalid parameter '%1' used in coordinate at argument '%2'",
8✔
1838
                                                 get_data_type_name(type), str));
8✔
1839
    }
76✔
1840
}
76✔
1841

1842
auto ParserDriver::cmp(const std::vector<ExpressionNode*>& values) -> std::pair<SubexprPtr, SubexprPtr>
1843
{
474,864✔
1844
    SubexprPtr left;
474,864✔
1845
    SubexprPtr right;
474,864✔
1846

237,420✔
1847
    auto left_is_constant = values[0]->is_constant();
474,864✔
1848
    auto right_is_constant = values[1]->is_constant();
474,864✔
1849

237,420✔
1850
    if (left_is_constant && right_is_constant) {
474,864✔
1851
        throw InvalidQueryError("Cannot compare two constants");
×
1852
    }
×
1853

237,420✔
1854
    if (right_is_constant) {
474,864✔
1855
        // Take left first - it cannot be a constant
235,428✔
1856
        left = values[0]->visit(this);
470,860✔
1857
        right = values[1]->visit(this, left->get_type());
470,860✔
1858
        verify_conditions(left.get(), right.get(), m_serializer_state);
470,860✔
1859
    }
470,860✔
1860
    else {
4,004✔
1861
        right = values[1]->visit(this);
4,004✔
1862
        if (left_is_constant) {
4,004✔
1863
            left = values[0]->visit(this, right->get_type());
1,488✔
1864
        }
1,488✔
1865
        else {
2,516✔
1866
            left = values[0]->visit(this);
2,516✔
1867
        }
2,516✔
1868
        verify_conditions(right.get(), left.get(), m_serializer_state);
4,004✔
1869
    }
4,004✔
1870
    return {std::move(left), std::move(right)};
474,864✔
1871
}
474,864✔
1872

1873
auto ParserDriver::column(LinkChain& link_chain, PathNode* path) -> SubexprPtr
1874
{
477,456✔
1875
    if (path->at_end()) {
477,456✔
1876
        // This is a link property. We can optimize by usingColumns<Link>.
898✔
1877
        // However Columns<Link> does not handle @keys and indexes
898✔
1878
        auto extended_col_key = link_chain.m_link_cols.back();
1,796✔
1879
        if (!extended_col_key.has_index()) {
1,796✔
1880
            return link_chain.create_subexpr<Link>(ColKey(extended_col_key));
1,792✔
1881
        }
1,792✔
1882
        link_chain.pop_back();
4✔
1883
        --path->current_path_elem;
4✔
1884
        --path->current_path_elem;
4✔
1885
    }
4✔
1886
    auto identifier = m_mapping.translate(link_chain, path->next_identifier());
476,560✔
1887
    if (auto col = link_chain.column(identifier, !path->at_end())) {
475,664✔
1888
        return col;
475,522✔
1889
    }
475,522✔
1890
    throw InvalidQueryError(
142✔
1891
        util::format("'%1' has no property '%2'", link_chain.get_current_table()->get_class_name(), identifier));
142✔
1892
}
142✔
1893

1894
void ParserDriver::backlink(LinkChain& link_chain, std::string_view raw_table_name, std::string_view raw_column_name)
1895
{
504✔
1896
    std::string table_name = m_mapping.translate_table_name(raw_table_name);
504✔
1897
    auto origin_table = m_base_table->get_parent_group()->get_table(table_name);
504✔
1898
    ColKey origin_column;
504✔
1899
    std::string column_name{raw_column_name};
504✔
1900
    if (origin_table) {
504✔
1901
        column_name = m_mapping.translate(origin_table, column_name);
496✔
1902
        origin_column = origin_table->get_column_key(column_name);
496✔
1903
    }
496✔
1904
    if (!origin_column) {
504✔
1905
        auto origin_table_name = Group::table_name_to_class_name(table_name);
12✔
1906
        auto current_table_name = link_chain.get_current_table()->get_class_name();
12✔
1907
        throw InvalidQueryError(util::format("No property '%1' found in type '%2' which links to type '%3'",
12✔
1908
                                             column_name, origin_table_name, current_table_name));
12✔
1909
    }
12✔
1910
    link_chain.backlink(*origin_table, origin_column);
492✔
1911
}
492✔
1912

1913
std::string ParserDriver::translate(const LinkChain& link_chain, const std::string& identifier)
1914
{
496,064✔
1915
    return m_mapping.translate(link_chain, identifier);
496,064✔
1916
}
496,064✔
1917

1918
int ParserDriver::parse(const std::string& str)
1919
{
45,914✔
1920
    // std::cout << str << std::endl;
22,950✔
1921
    parse_buffer.append(str);
45,914✔
1922
    parse_buffer.append("\0\0", 2); // Flex requires 2 terminating zeroes
45,914✔
1923
    scan_begin(m_yyscanner, trace_scanning);
45,914✔
1924
    yy::parser parse(*this, m_yyscanner);
45,914✔
1925
    parse.set_debug_level(trace_parsing);
45,914✔
1926
    int res = parse();
45,914✔
1927
    if (parse_error) {
45,914✔
1928
        throw SyntaxError(util::format("Invalid predicate: '%1': %2", str, error_string));
724✔
1929
    }
724✔
1930
    return res;
45,190✔
1931
}
45,190✔
1932

1933
void parse(const std::string& str)
1934
{
1,004✔
1935
    ParserDriver driver;
1,004✔
1936
    driver.parse(str);
1,004✔
1937
}
1,004✔
1938

1939
std::string check_escapes(const char* str)
1940
{
497,044✔
1941
    std::string ret;
497,044✔
1942
    const char* p = strchr(str, '\\');
497,044✔
1943
    while (p) {
497,468✔
1944
        ret += std::string(str, p);
424✔
1945
        p++;
424✔
1946
        if (*p == ' ') {
424✔
1947
            ret += ' ';
144✔
1948
        }
144✔
1949
        else if (*p == 't') {
280✔
1950
            ret += '\t';
280✔
1951
        }
280✔
1952
        else if (*p == 'r') {
×
1953
            ret += '\r';
×
1954
        }
×
1955
        else if (*p == 'n') {
×
1956
            ret += '\n';
×
1957
        }
×
1958
        str = p + 1;
424✔
1959
        p = strchr(str, '\\');
424✔
1960
    }
424✔
1961
    return ret + std::string(str);
497,044✔
1962
}
497,044✔
1963

1964
} // namespace query_parser
1965

1966
Query Table::query(const std::string& query_string, const std::vector<MixedArguments::Arg>& arguments) const
1967
{
11,588✔
1968
    MixedArguments args(arguments);
11,588✔
1969
    return query(query_string, args, {});
11,588✔
1970
}
11,588✔
1971

1972
Query Table::query(const std::string& query_string, const std::vector<Mixed>& arguments) const
1973
{
8✔
1974
    MixedArguments args(arguments);
8✔
1975
    return query(query_string, args, {});
8✔
1976
}
8✔
1977

1978
Query Table::query(const std::string& query_string, const std::vector<Mixed>& arguments,
1979
                   const query_parser::KeyPathMapping& mapping) const
1980
{
1,688✔
1981
    MixedArguments args(arguments);
1,688✔
1982
    return query(query_string, args, mapping);
1,688✔
1983
}
1,688✔
1984

1985
Query Table::query(const std::string& query_string, const std::vector<MixedArguments::Arg>& arguments,
1986
                   const query_parser::KeyPathMapping& mapping) const
1987
{
320✔
1988
    MixedArguments args(arguments);
320✔
1989
    return query(query_string, args, mapping);
320✔
1990
}
320✔
1991

1992
Query Table::query(const std::string& query_string, query_parser::Arguments& args,
1993
                   const query_parser::KeyPathMapping& mapping) const
1994
{
44,902✔
1995
    ParserDriver driver(m_own_ref, args, mapping);
44,902✔
1996
    driver.parse(query_string);
44,902✔
1997
    driver.result->canonicalize();
44,902✔
1998
    return driver.result->visit(&driver).set_ordering(driver.ordering->visit(&driver));
44,902✔
1999
}
44,902✔
2000

2001
std::unique_ptr<Subexpr> LinkChain::column(const std::string& col, bool has_path)
2002
{
477,388✔
2003
    auto col_key = m_current_table->get_column_key(col);
477,388✔
2004
    if (!col_key) {
477,388✔
2005
        return nullptr;
128✔
2006
    }
128✔
2007

238,632✔
2008
    auto col_type{col_key.get_type()};
477,260✔
2009
    if (col_key.is_dictionary()) {
477,260✔
2010
        return create_subexpr<Dictionary>(col_key);
952✔
2011
    }
952✔
2012
    if (Table::is_link_type(col_type)) {
476,308✔
2013
        add(col_key);
×
2014
        return create_subexpr<Link>(col_key);
×
2015
    }
×
2016

238,156✔
2017
    if (col_key.is_set()) {
476,308✔
2018
        switch (col_type) {
3,984✔
2019
            case col_type_Int:
416✔
2020
                return create_subexpr<Set<Int>>(col_key);
416✔
2021
            case col_type_Bool:
✔
2022
                return create_subexpr<Set<Bool>>(col_key);
×
2023
            case col_type_String:
512✔
2024
                return create_subexpr<Set<String>>(col_key);
512✔
2025
            case col_type_Binary:
512✔
2026
                return create_subexpr<Set<Binary>>(col_key);
512✔
2027
            case col_type_Float:
416✔
2028
                return create_subexpr<Set<Float>>(col_key);
416✔
2029
            case col_type_Double:
416✔
2030
                return create_subexpr<Set<Double>>(col_key);
416✔
2031
            case col_type_Timestamp:
248✔
2032
                return create_subexpr<Set<Timestamp>>(col_key);
248✔
2033
            case col_type_Decimal:
416✔
2034
                return create_subexpr<Set<Decimal>>(col_key);
416✔
2035
            case col_type_UUID:
248✔
2036
                return create_subexpr<Set<UUID>>(col_key);
248✔
2037
            case col_type_ObjectId:
248✔
2038
                return create_subexpr<Set<ObjectId>>(col_key);
248✔
2039
            case col_type_Mixed:
552✔
2040
                return create_subexpr<Set<Mixed>>(col_key);
552✔
2041
            default:
✔
2042
                break;
×
2043
        }
472,324✔
2044
    }
472,324✔
2045
    else if (col_key.is_list()) {
472,324✔
2046
        switch (col_type) {
16,460✔
2047
            case col_type_Int:
3,040✔
2048
                return create_subexpr<Lst<Int>>(col_key);
3,040✔
2049
            case col_type_Bool:
880✔
2050
                return create_subexpr<Lst<Bool>>(col_key);
880✔
2051
            case col_type_String:
5,024✔
2052
                return create_subexpr<Lst<String>>(col_key);
5,024✔
2053
            case col_type_Binary:
1,660✔
2054
                return create_subexpr<Lst<Binary>>(col_key);
1,660✔
2055
            case col_type_Float:
880✔
2056
                return create_subexpr<Lst<Float>>(col_key);
880✔
2057
            case col_type_Double:
880✔
2058
                return create_subexpr<Lst<Double>>(col_key);
880✔
2059
            case col_type_Timestamp:
880✔
2060
                return create_subexpr<Lst<Timestamp>>(col_key);
880✔
2061
            case col_type_Decimal:
880✔
2062
                return create_subexpr<Lst<Decimal>>(col_key);
880✔
2063
            case col_type_UUID:
880✔
2064
                return create_subexpr<Lst<UUID>>(col_key);
880✔
2065
            case col_type_ObjectId:
880✔
2066
                return create_subexpr<Lst<ObjectId>>(col_key);
880✔
2067
            case col_type_Mixed:
576✔
2068
                return create_subexpr<Lst<Mixed>>(col_key);
576✔
2069
            default:
✔
2070
                break;
×
2071
        }
455,864✔
2072
    }
455,864✔
2073
    else {
455,864✔
2074
        // Having a path implies a collection
227,934✔
2075
        if (m_comparison_type && !has_path) {
455,864✔
2076
            bool has_list = false;
1,260✔
2077
            for (ColKey link_key : m_link_cols) {
1,312✔
2078
                if (link_key.is_collection() || link_key.get_type() == col_type_BackLink) {
1,312✔
2079
                    has_list = true;
1,216✔
2080
                    break;
1,216✔
2081
                }
1,216✔
2082
            }
1,312✔
2083
            if (!has_list) {
1,260✔
2084
                throw InvalidQueryError(util::format("The keypath following '%1' must contain a list",
44✔
2085
                                                     expression_cmp_type_to_str(m_comparison_type)));
44✔
2086
            }
44✔
2087
        }
455,820✔
2088

227,912✔
2089
        switch (col_type) {
455,820✔
2090
            case col_type_Int:
13,680✔
2091
                return create_subexpr<Int>(col_key);
13,680✔
2092
            case col_type_Bool:
224✔
2093
                return create_subexpr<Bool>(col_key);
224✔
2094
            case col_type_String:
4,544✔
2095
                return create_subexpr<String>(col_key);
4,544✔
2096
            case col_type_Binary:
2,052✔
2097
                return create_subexpr<Binary>(col_key);
2,052✔
2098
            case col_type_Float:
552✔
2099
                return create_subexpr<Float>(col_key);
552✔
2100
            case col_type_Double:
1,944✔
2101
                return create_subexpr<Double>(col_key);
1,944✔
2102
            case col_type_Timestamp:
728✔
2103
                return create_subexpr<Timestamp>(col_key);
728✔
2104
            case col_type_Decimal:
1,460✔
2105
                return create_subexpr<Decimal>(col_key);
1,460✔
2106
            case col_type_UUID:
520✔
2107
                return create_subexpr<UUID>(col_key);
520✔
2108
            case col_type_ObjectId:
428,804✔
2109
                return create_subexpr<ObjectId>(col_key);
428,804✔
2110
            case col_type_Mixed:
1,292✔
2111
                return create_subexpr<Mixed>(col_key);
1,292✔
2112
            default:
✔
2113
                break;
×
2114
        }
×
2115
    }
×
2116
    REALM_UNREACHABLE();
2117
    return nullptr;
×
2118
}
×
2119

2120
std::unique_ptr<Subexpr> LinkChain::subquery(Query subquery)
2121
{
280✔
2122
    REALM_ASSERT(m_link_cols.size() > 0);
280✔
2123
    auto col_key = m_link_cols.back();
280✔
2124
    return std::make_unique<SubQueryCount>(subquery, Columns<Link>(col_key, m_base_table, m_link_cols).link_map());
280✔
2125
}
280✔
2126

2127
template <class T>
2128
SubQuery<T> column(const Table& origin, ColKey origin_col_key, Query subquery)
2129
{
2130
    static_assert(std::is_same<T, BackLink>::value, "A subquery must involve a link list or backlink column");
2131
    return SubQuery<T>(column<T>(origin, origin_col_key), std::move(subquery));
2132
}
2133

2134
} // namespace realm
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

© 2026 Coveralls, Inc