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

realm / realm-core / 2476

05 Jul 2024 01:18PM UTC coverage: 90.974% (-0.02%) from 90.995%
2476

push

Evergreen

web-flow
New changelog section to prepare for vNext (#7866)

102302 of 180462 branches covered (56.69%)

215189 of 236538 relevant lines covered (90.97%)

5815746.14 hits per line

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

87.57
/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
    }
272✔
58
    return "";
×
59
}
272✔
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
        }
44✔
72
    }
44✔
73
    return "";
×
74
}
44✔
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 (...) {
44✔
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,720!
100
           (s[5] == 'h' || s[5] == 'H');
1,720!
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
}
32✔
128

129
template <typename T>
130
inline std::optional<T> string_to(const std::string& s)
131
{
436✔
132
    std::istringstream iss(s);
436✔
133
    iss.imbue(std::locale::classic());
436✔
134
    T value;
436✔
135
    iss >> value;
436✔
136
    if (iss.fail()) {
436✔
137
        if (!try_parse_specials(s, value)) {
32✔
138
            return {};
32✔
139
        }
32✔
140
    }
32✔
141
    return value;
404✔
142
}
436✔
143

144
template <>
145
inline std::optional<Decimal128> string_to<Decimal128>(const std::string& s)
146
{
4✔
147
    Decimal128 value(s);
4✔
148
    if (value.is_nan()) {
4✔
149
        return {};
4✔
150
    }
4✔
151
    return value;
×
152
}
4✔
153

154
class MixedArguments : public query_parser::Arguments {
155
public:
156
    using Arg = mpark::variant<Mixed, std::vector<Mixed>>;
157

158
    MixedArguments(const std::vector<Mixed>& args)
159
        : Arguments(args.size())
904✔
160
        , m_args([](const std::vector<Mixed>& args) -> std::vector<Arg> {
1,808✔
161
            std::vector<Arg> ret;
1,808✔
162
            ret.reserve(args.size());
1,808✔
163
            for (const Mixed& m : args) {
15,852✔
164
                ret.push_back(m);
15,852✔
165
            }
15,852✔
166
            return ret;
1,808✔
167
        }(args))
1,808✔
168
    {
1,808✔
169
    }
1,808✔
170
    MixedArguments(const std::vector<Arg>& args)
171
        : Arguments(args.size())
6,282✔
172
        , m_args(args)
6,282✔
173
    {
12,564✔
174
    }
12,564✔
175
    bool bool_for_argument(size_t n) final
176
    {
×
177
        return mixed_for_argument(n).get<bool>();
×
178
    }
×
179
    long long long_for_argument(size_t n) final
180
    {
×
181
        return mixed_for_argument(n).get<int64_t>();
×
182
    }
×
183
    float float_for_argument(size_t n) final
184
    {
×
185
        return mixed_for_argument(n).get<float>();
×
186
    }
×
187
    double double_for_argument(size_t n) final
188
    {
68✔
189
        return mixed_for_argument(n).get<double>();
68✔
190
    }
68✔
191
    StringData string_for_argument(size_t n) final
192
    {
20✔
193
        return mixed_for_argument(n).get<StringData>();
20✔
194
    }
20✔
195
    BinaryData binary_for_argument(size_t n) final
196
    {
×
197
        return mixed_for_argument(n).get<BinaryData>();
×
198
    }
×
199
    Timestamp timestamp_for_argument(size_t n) final
200
    {
×
201
        return mixed_for_argument(n).get<Timestamp>();
×
202
    }
×
203
    ObjectId objectid_for_argument(size_t n) final
204
    {
×
205
        return mixed_for_argument(n).get<ObjectId>();
×
206
    }
×
207
    UUID uuid_for_argument(size_t n) final
208
    {
×
209
        return mixed_for_argument(n).get<UUID>();
×
210
    }
×
211
    Decimal128 decimal128_for_argument(size_t n) final
212
    {
×
213
        return mixed_for_argument(n).get<Decimal128>();
×
214
    }
×
215
    ObjKey object_index_for_argument(size_t n) final
216
    {
×
217
        return mixed_for_argument(n).get<ObjKey>();
×
218
    }
×
219
    ObjLink objlink_for_argument(size_t n) final
220
    {
×
221
        return mixed_for_argument(n).get<ObjLink>();
×
222
    }
×
223
#if REALM_ENABLE_GEOSPATIAL
224
    Geospatial geospatial_for_argument(size_t n) final
225
    {
16✔
226
        return mixed_for_argument(n).get<Geospatial>();
16✔
227
    }
16✔
228
#endif
229
    std::vector<Mixed> list_for_argument(size_t n) final
230
    {
116✔
231
        Arguments::verify_ndx(n);
116✔
232
        return mpark::get<std::vector<Mixed>>(m_args[n]);
116✔
233
    }
116✔
234
    bool is_argument_null(size_t n) final
235
    {
428,888✔
236
        Arguments::verify_ndx(n);
428,888✔
237
        return visit(util::overload{
428,888✔
238
                         [](const Mixed& m) {
428,888✔
239
                             return m.is_null();
428,888✔
240
                         },
428,888✔
241
                         [](const std::vector<Mixed>&) {
428,888✔
242
                             return false;
×
243
                         },
×
244
                     },
428,888✔
245
                     m_args[n]);
428,888✔
246
    }
428,888✔
247
    bool is_argument_list(size_t n) final
248
    {
857,904✔
249
        Arguments::verify_ndx(n);
857,904✔
250
        static_assert(std::is_same_v<mpark::variant_alternative_t<1, Arg>, std::vector<Mixed>>);
857,904✔
251
        return m_args[n].index() == 1;
857,904✔
252
    }
857,904✔
253
    DataType type_for_argument(size_t n) final
254
    {
120✔
255
        return mixed_for_argument(n).get_type();
120✔
256
    }
120✔
257

258
    Mixed mixed_for_argument(size_t n) final
259
    {
428,940✔
260
        Arguments::verify_ndx(n);
428,940✔
261
        if (is_argument_list(n)) {
428,940✔
262
            throw InvalidQueryArgError(
×
263
                util::format("Request for scalar argument at index %1 but a list was provided", n));
×
264
        }
×
265

266
        return mpark::get<Mixed>(m_args[n]);
428,940✔
267
    }
428,940✔
268

269
private:
270
    const std::vector<Arg> m_args;
271
};
272

273
Timestamp get_timestamp_if_valid(int64_t seconds, int32_t nanoseconds)
274
{
604✔
275
    const bool both_non_negative = seconds >= 0 && nanoseconds >= 0;
604✔
276
    const bool both_non_positive = seconds <= 0 && nanoseconds <= 0;
604✔
277
    if (both_non_negative || both_non_positive) {
604✔
278
        return Timestamp(seconds, nanoseconds);
588✔
279
    }
588✔
280
    throw SyntaxError("Invalid timestamp format");
16✔
281
}
604✔
282

283
} // namespace
284

285
namespace realm {
286

287
namespace query_parser {
288

289
std::string_view string_for_op(CompareType op)
290
{
3,052✔
291
    switch (op) {
3,052✔
292
        case CompareType::EQUAL:
164✔
293
            return "=";
164✔
294
        case CompareType::NOT_EQUAL:
40✔
295
            return "!=";
40✔
296
        case CompareType::GREATER:
✔
297
            return ">";
×
298
        case CompareType::LESS:
✔
299
            return "<";
×
300
        case CompareType::GREATER_EQUAL:
✔
301
            return ">=";
×
302
        case CompareType::LESS_EQUAL:
✔
303
            return "<=";
×
304
        case CompareType::BEGINSWITH:
632✔
305
            return "beginswith";
632✔
306
        case CompareType::ENDSWITH:
616✔
307
            return "endswith";
616✔
308
        case CompareType::CONTAINS:
968✔
309
            return "contains";
968✔
310
        case CompareType::LIKE:
584✔
311
            return "like";
584✔
312
        case CompareType::IN:
8✔
313
            return "in";
8✔
314
        case CompareType::TEXT:
40✔
315
            return "text";
40✔
316
    }
3,052✔
317
    return ""; // appease MSVC warnings
×
318
}
3,052✔
319

320
NoArguments ParserDriver::s_default_args;
321
query_parser::KeyPathMapping ParserDriver::s_default_mapping;
322

323
ParserNode::~ParserNode() = default;
2,411,172✔
324

325
QueryNode::~QueryNode() = default;
910,376✔
326

327
Query NotNode::visit(ParserDriver* drv)
328
{
1,232✔
329
    Query q = drv->m_base_table->where();
1,232✔
330
    q.Not();
1,232✔
331
    q.and_query(query->visit(drv));
1,232✔
332
    return {q};
1,232✔
333
}
1,232✔
334

335
Query OrNode::visit(ParserDriver* drv)
336
{
584✔
337
    Query q(drv->m_base_table);
584✔
338
    q.group();
584✔
339
    for (auto it : children) {
430,924✔
340
        q.Or();
430,924✔
341
        q.and_query(it->visit(drv));
430,924✔
342
    }
430,924✔
343
    q.end_group();
584✔
344

345
    return q;
584✔
346
}
584✔
347

348
Query AndNode::visit(ParserDriver* drv)
349
{
660✔
350
    Query q(drv->m_base_table);
660✔
351
    for (auto it : children) {
1,496✔
352
        q.and_query(it->visit(drv));
1,496✔
353
    }
1,496✔
354
    return q;
660✔
355
}
660✔
356

357
static void verify_only_string_types(DataType type, std::string_view op_string)
358
{
3,052✔
359
    if (type != type_String && type != type_Binary && type != type_Mixed) {
3,052✔
360
        throw InvalidQueryError(util::format(
348✔
361
            "Unsupported comparison operator '%1' against type '%2', right side must be a string or binary type",
348✔
362
            op_string, get_data_type_name(type)));
348✔
363
    }
348✔
364
}
3,052✔
365

366
std::unique_ptr<Subexpr> OperationNode::visit(ParserDriver* drv, DataType type)
367
{
1,344✔
368
    std::unique_ptr<Subexpr> left;
1,344✔
369
    std::unique_ptr<Subexpr> right;
1,344✔
370

371
    const bool left_is_constant = m_left->is_constant();
1,344✔
372
    const bool right_is_constant = m_right->is_constant();
1,344✔
373
    const bool produces_multiple_values = m_left->is_list() || m_right->is_list();
1,344✔
374

375
    if (left_is_constant && right_is_constant && !produces_multiple_values) {
1,344✔
376
        right = m_right->visit(drv, type);
48✔
377
        left = m_left->visit(drv, type);
48✔
378
        auto v_left = left->get_mixed();
48✔
379
        auto v_right = right->get_mixed();
48✔
380
        Mixed result;
48✔
381
        switch (m_op) {
48✔
382
            case '+':
28✔
383
                result = v_left + v_right;
28✔
384
                break;
28✔
385
            case '-':
✔
386
                result = v_left - v_right;
×
387
                break;
×
388
            case '*':
20✔
389
                result = v_left * v_right;
20✔
390
                break;
20✔
391
            case '/':
✔
392
                result = v_left / v_right;
×
393
                break;
×
394
            default:
✔
395
                break;
×
396
        }
48✔
397
        return std::make_unique<Value<Mixed>>(result);
48✔
398
    }
48✔
399

400
    if (right_is_constant) {
1,296✔
401
        // Take left first - it cannot be a constant
402
        left = m_left->visit(drv);
512✔
403

404
        right = m_right->visit(drv, left->get_type());
512✔
405
    }
512✔
406
    else {
784✔
407
        right = m_right->visit(drv);
784✔
408
        if (left_is_constant) {
784✔
409
            left = m_left->visit(drv, right->get_type());
152✔
410
        }
152✔
411
        else {
632✔
412
            left = m_left->visit(drv);
632✔
413
        }
632✔
414
    }
784✔
415
    if (!Mixed::is_numeric(left->get_type(), right->get_type())) {
1,296✔
416
        util::serializer::SerialisationState state;
16✔
417
        std::string op(&m_op, 1);
16✔
418
        throw InvalidQueryArgError(util::format("Cannot perform '%1' operation on '%2' and '%3'", op,
16✔
419
                                                left->description(state), right->description(state)));
16✔
420
    }
16✔
421

422
    switch (m_op) {
1,280✔
423
        case '+':
544✔
424
            return std::make_unique<Operator<Plus>>(std::move(left), std::move(right));
544✔
425
        case '-':
216✔
426
            return std::make_unique<Operator<Minus>>(std::move(left), std::move(right));
216✔
427
        case '*':
292✔
428
            return std::make_unique<Operator<Mul>>(std::move(left), std::move(right));
292✔
429
        case '/':
228✔
430
            return std::make_unique<Operator<Div>>(std::move(left), std::move(right));
228✔
431
        default:
✔
432
            break;
×
433
    }
1,280✔
434
    return {};
×
435
}
1,280✔
436

437
Query EqualityNode::visit(ParserDriver* drv)
438
{
467,898✔
439
    auto [left, right] = drv->cmp(values);
467,898✔
440

441
    auto left_type = left->get_type();
467,898✔
442
    auto right_type = right->get_type();
467,898✔
443

444
    auto handle_typed_links = [drv](std::unique_ptr<Subexpr>& list, std::unique_ptr<Subexpr>& expr, DataType& type) {
467,898✔
445
        if (auto link_column = dynamic_cast<const Columns<Link>*>(list.get())) {
400✔
446
            // Change all TypedLink values to ObjKey values
447
            auto value = dynamic_cast<ValueBase*>(expr.get());
400✔
448
            auto left_dest_table_key = link_column->link_map().get_target_table()->get_key();
400✔
449
            auto sz = value->size();
400✔
450
            auto obj_keys = std::make_unique<Value<ObjKey>>();
400✔
451
            obj_keys->init(expr->has_multiple_values(), sz);
400✔
452
            obj_keys->set_comparison_type(expr->get_comparison_type());
400✔
453
            for (size_t i = 0; i < sz; i++) {
808✔
454
                auto val = value->get(i);
416✔
455
                // i'th entry is already NULL
456
                if (!val.is_null()) {
416✔
457
                    TableKey right_table_key;
200✔
458
                    ObjKey right_obj_key;
200✔
459
                    if (val.is_type(type_Link)) {
200✔
460
                        right_table_key = left_dest_table_key;
20✔
461
                        right_obj_key = val.get<ObjKey>();
20✔
462
                    }
20✔
463
                    else if (val.is_type(type_TypedLink)) {
180✔
464
                        right_table_key = val.get_link().get_table_key();
180✔
465
                        right_obj_key = val.get_link().get_obj_key();
180✔
466
                    }
180✔
467
                    else {
×
468
                        const char* target_type = get_data_type_name(val.get_type());
×
469
                        throw InvalidQueryError(
×
470
                            util::format("Unsupported comparison between '%1' and type '%2'",
×
471
                                         link_column->link_map().description(drv->m_serializer_state), target_type));
×
472
                    }
×
473
                    if (left_dest_table_key == right_table_key) {
200✔
474
                        obj_keys->set(i, right_obj_key);
192✔
475
                    }
192✔
476
                    else {
8✔
477
                        const Group* g = drv->m_base_table->get_parent_group();
8✔
478
                        throw InvalidQueryArgError(
8✔
479
                            util::format("The relationship '%1' which links to type '%2' cannot be compared to "
8✔
480
                                         "an argument of type %3",
8✔
481
                                         link_column->link_map().description(drv->m_serializer_state),
8✔
482
                                         link_column->link_map().get_target_table()->get_class_name(),
8✔
483
                                         print_pretty_objlink(ObjLink(right_table_key, right_obj_key), g)));
8✔
484
                    }
8✔
485
                }
200✔
486
            }
416✔
487
            expr = std::move(obj_keys);
392✔
488
            type = type_Link;
392✔
489
        }
392✔
490
    };
400✔
491

492
    if (left_type == type_Link && right->has_constant_evaluation()) {
467,898✔
493
        handle_typed_links(left, right, right_type);
388✔
494
    }
388✔
495
    if (right_type == type_Link && left->has_constant_evaluation()) {
467,898✔
496
        handle_typed_links(right, left, left_type);
12✔
497
    }
12✔
498

499
    if (left_type.is_valid() && right_type.is_valid() && !Mixed::data_types_are_comparable(left_type, right_type)) {
467,898✔
500
        throw InvalidQueryError(util::format("Unsupported comparison between type '%1' and type '%2'",
24✔
501
                                             get_data_type_name(left_type), get_data_type_name(right_type)));
24✔
502
    }
24✔
503
    if (left_type == type_TypeOfValue || right_type == type_TypeOfValue) {
467,874✔
504
        if (left_type != right_type) {
696✔
505
            throw InvalidQueryArgError(
12✔
506
                util::format("Unsupported comparison between @type and raw value: '%1' and '%2'",
12✔
507
                             get_data_type_name(left_type), get_data_type_name(right_type)));
12✔
508
        }
12✔
509
    }
696✔
510

511
    if (op == CompareType::IN) {
467,862✔
512
        Subexpr* r = right.get();
848✔
513
        if (!r->has_multiple_values()) {
848✔
514
            throw InvalidQueryArgError("The keypath following 'IN' must contain a list. Found '" +
8✔
515
                                       r->description(drv->m_serializer_state) + "'");
8✔
516
        }
8✔
517
    }
848✔
518

519
    if (op == CompareType::IN || op == CompareType::EQUAL) {
467,854✔
520
        if (auto mixed_list = dynamic_cast<ConstantMixedList*>(right.get());
463,356✔
521
            mixed_list && mixed_list->size() &&
463,356✔
522
            mixed_list->get_comparison_type().value_or(ExpressionComparisonType::Any) ==
463,356✔
523
                ExpressionComparisonType::Any) {
928✔
524
            if (auto lhs = dynamic_cast<ObjPropertyBase*>(left.get());
616✔
525
                lhs && lhs->column_key() && !lhs->column_key().is_collection() && !lhs->links_exist() &&
616✔
526
                lhs->column_key().get_type() != col_type_Mixed) {
616✔
527
                return drv->m_base_table->where().in(lhs->column_key(), mixed_list->begin(), mixed_list->end());
344✔
528
            }
344✔
529
        }
616✔
530
    }
463,356✔
531

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

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

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

641
    auto& min(limits->elements.at(0));
124✔
642
    auto& max(limits->elements.at(1));
124✔
643
    Query q(drv->m_base_table);
124✔
644

645
    auto tmp = prop->visit(drv);
124✔
646
    const ObjPropertyBase* obj_prop = dynamic_cast<const ObjPropertyBase*>(tmp.get());
124✔
647
    if (obj_prop) {
124✔
648
        if (tmp->get_type() == type_Int) {
116✔
649
            auto min_val = min->visit(drv, type_Int);
96✔
650
            auto max_val = max->visit(drv, type_Int);
96✔
651
            q.between(obj_prop->column_key(), min_val->get_mixed().get_int(), max_val->get_mixed().get_int());
96✔
652
            return q;
96✔
653
        }
96✔
654
        if (tmp->get_type() == type_Timestamp) {
20✔
655
            auto min_val = min->visit(drv, type_Timestamp);
4✔
656
            auto max_val = max->visit(drv, type_Timestamp);
4✔
657
            q.between(obj_prop->column_key(), min_val->get_mixed().get_timestamp(),
4✔
658
                      max_val->get_mixed().get_timestamp());
4✔
659
            return q;
4✔
660
        }
4✔
661
    }
20✔
662

663
    RelationalNode cmp1(prop, CompareType::GREATER_EQUAL, min);
24✔
664
    RelationalNode cmp2(prop, CompareType::LESS_EQUAL, max);
24✔
665

666
    q.and_query(cmp1.visit(drv));
24✔
667
    q.and_query(cmp2.visit(drv));
24✔
668

669
    return q;
24✔
670
}
124✔
671

672
Query RelationalNode::visit(ParserDriver* drv)
673
{
5,148✔
674
    auto [left, right] = drv->cmp(values);
5,148✔
675

676
    auto left_type = left->get_type();
5,148✔
677
    auto right_type = right->get_type();
5,148✔
678
    const bool right_type_is_null = right->has_single_value() && right->get_mixed().is_null();
5,148✔
679
    const bool left_type_is_null = left->has_single_value() && left->get_mixed().is_null();
5,148✔
680
    REALM_ASSERT(!(left_type_is_null && right_type_is_null));
5,148✔
681

682
    if (left_type == type_Link || left_type == type_TypeOfValue) {
5,148✔
683
        throw InvalidQueryError(util::format(
×
684
            "Unsupported operator %1 in query. Only equal (==) and not equal (!=) are supported for this type.",
×
685
            string_for_op(op)));
×
686
    }
×
687

688
    if (!(left_type_is_null || right_type_is_null) && (!left_type.is_valid() || !right_type.is_valid() ||
5,148✔
689
                                                       !Mixed::data_types_are_comparable(left_type, right_type))) {
4,616✔
690
        throw InvalidQueryError(util::format("Unsupported comparison between type '%1' and type '%2'",
×
691
                                             get_data_type_name(left_type), get_data_type_name(right_type)));
×
692
    }
×
693

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

746
Query StringOpsNode::visit(ParserDriver* drv)
747
{
2,856✔
748
    auto [left, right] = drv->cmp(values);
2,856✔
749

750
    auto left_type = left->get_type();
2,856✔
751
    auto right_type = right->get_type();
2,856✔
752
    const ObjPropertyBase* prop = dynamic_cast<const ObjPropertyBase*>(left.get());
2,856✔
753

754
    verify_only_string_types(right_type, string_for_op(op));
2,856✔
755

756
    if (prop && !prop->links_exist() && !prop->has_path() && right->has_single_value() &&
2,856✔
757
        (left_type == right_type || left_type == type_Mixed)) {
2,856✔
758
        auto col_key = prop->column_key();
548✔
759
        if (right_type == type_String) {
548✔
760
            StringData val = right->get_mixed().get_string();
332✔
761

762
            switch (op) {
332✔
763
                case CompareType::BEGINSWITH:
56✔
764
                    return drv->m_base_table->where().begins_with(col_key, val, case_sensitive);
56✔
765
                case CompareType::ENDSWITH:
64✔
766
                    return drv->m_base_table->where().ends_with(col_key, val, case_sensitive);
64✔
767
                case CompareType::CONTAINS:
120✔
768
                    return drv->m_base_table->where().contains(col_key, val, case_sensitive);
120✔
769
                case CompareType::LIKE:
56✔
770
                    return drv->m_base_table->where().like(col_key, val, case_sensitive);
56✔
771
                case CompareType::TEXT:
36✔
772
                    return drv->m_base_table->where().fulltext(col_key, val);
36✔
773
                case CompareType::IN:
✔
774
                case CompareType::EQUAL:
✔
775
                case CompareType::NOT_EQUAL:
✔
776
                case CompareType::GREATER:
✔
777
                case CompareType::LESS:
✔
778
                case CompareType::GREATER_EQUAL:
✔
779
                case CompareType::LESS_EQUAL:
✔
780
                    break;
×
781
            }
332✔
782
        }
332✔
783
        else if (right_type == type_Binary) {
216✔
784
            BinaryData val = right->get_mixed().get_binary();
216✔
785

786
            switch (op) {
216✔
787
                case CompareType::BEGINSWITH:
48✔
788
                    return drv->m_base_table->where().begins_with(col_key, val, case_sensitive);
48✔
789
                case CompareType::ENDSWITH:
56✔
790
                    return drv->m_base_table->where().ends_with(col_key, val, case_sensitive);
56✔
791
                case CompareType::CONTAINS:
72✔
792
                    return drv->m_base_table->where().contains(col_key, val, case_sensitive);
72✔
793
                case CompareType::LIKE:
40✔
794
                    return drv->m_base_table->where().like(col_key, val, case_sensitive);
40✔
795
                case CompareType::TEXT:
✔
796
                case CompareType::IN:
✔
797
                case CompareType::EQUAL:
✔
798
                case CompareType::NOT_EQUAL:
✔
799
                case CompareType::GREATER:
✔
800
                case CompareType::LESS:
✔
801
                case CompareType::GREATER_EQUAL:
✔
802
                case CompareType::LESS_EQUAL:
✔
803
                    break;
×
804
            }
216✔
805
        }
216✔
806
    }
548✔
807

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

860
#if REALM_ENABLE_GEOSPATIAL
861
Query GeoWithinNode::visit(ParserDriver* drv)
862
{
176✔
863
    auto left = prop->visit(drv);
176✔
864
    auto left_type = left->get_type();
176✔
865
    if (left_type != type_Link) {
176✔
866
        throw InvalidQueryError(util::format("The left hand side of 'geoWithin' must be a link to geoJSON formatted "
4✔
867
                                             "data. But the provided type is '%1'",
4✔
868
                                             get_data_type_name(left_type)));
4✔
869
    }
4✔
870
    auto link_column = dynamic_cast<const Columns<Link>*>(left.get());
172✔
871

872
    if (geo) {
172✔
873
        auto right = geo->visit(drv, type_Int);
124✔
874
        auto geo_value = dynamic_cast<const ConstantGeospatialValue*>(right.get());
124✔
875
        return link_column->geo_within(geo_value->get_mixed().get<Geospatial>());
124✔
876
    }
124✔
877

878
    REALM_ASSERT_3(argument.size(), >, 1);
48✔
879
    REALM_ASSERT_3(argument[0], ==, '$');
48✔
880
    size_t arg_no = size_t(strtol(argument.substr(1).c_str(), nullptr, 10));
48✔
881
    auto right_type = drv->m_args.is_argument_null(arg_no) ? DataType(-1) : drv->m_args.type_for_argument(arg_no);
48✔
882

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

926
    if (geo_from_argument.get_type() == Geospatial::Type::Invalid) {
28✔
927
        throw InvalidQueryError(util::format(
4✔
928
            "The right hand side of 'geoWithin' must be a valid Geospatial value, got '%1'", geo_from_argument));
4✔
929
    }
4✔
930
    Status geo_status = geo_from_argument.is_valid();
24✔
931
    if (!geo_status.is_ok()) {
24✔
932
        throw InvalidQueryError(
×
933
            util::format("The Geospatial query argument region is invalid: '%1'", geo_status.reason()));
×
934
    }
×
935
    return link_column->geo_within(geo_from_argument);
24✔
936
}
24✔
937
#endif
938

939
Query TrueOrFalseNode::visit(ParserDriver* drv)
940
{
612✔
941
    Query q = drv->m_base_table->where();
612✔
942
    if (true_or_false) {
612✔
943
        q.and_query(std::unique_ptr<realm::Expression>(new TrueExpression));
460✔
944
    }
460✔
945
    else {
152✔
946
        q.and_query(std::unique_ptr<realm::Expression>(new FalseExpression));
152✔
947
    }
152✔
948
    return q;
612✔
949
}
612✔
950

951
std::unique_ptr<Subexpr> PropertyNode::visit(ParserDriver* drv, DataType)
952
{
479,228✔
953
    path->resolve_arg(drv);
479,228✔
954
    if (path->path_elems.back().is_key() && path->path_elems.back().get_key() == "@links") {
479,228✔
955
        identifier = "@links";
304✔
956
        // This is a backlink aggregate query
957
        path->path_elems.pop_back();
304✔
958
        auto link_chain = path->visit(drv, comp_type);
304✔
959
        auto sub = link_chain.get_backlink_count<Int>();
304✔
960
        return sub.clone();
304✔
961
    }
304✔
962
    m_link_chain = path->visit(drv, comp_type);
478,924✔
963
    if (!path->at_end()) {
478,924✔
964
        if (!path->current_path_elem->is_key()) {
477,104✔
965
            throw InvalidQueryError(util::format("[%1] not expected", *path->current_path_elem));
4✔
966
        }
4✔
967
        identifier = path->current_path_elem->get_key();
477,100✔
968
    }
477,100✔
969
    std::unique_ptr<Subexpr> subexpr{drv->column(m_link_chain, path)};
478,920✔
970

971
    Path indexes;
478,920✔
972
    while (!path->at_end()) {
484,428✔
973
        indexes.emplace_back(std::move(*(path->current_path_elem++)));
5,508✔
974
    }
5,508✔
975

976
    if (!indexes.empty()) {
478,920✔
977
        auto ok = false;
4,152✔
978
        const PathElement& first_index = indexes.front();
4,152✔
979
        if (indexes.size() > 1 && subexpr->get_type() != type_Mixed) {
4,152✔
980
            throw InvalidQueryError("Only Property of type 'any' can have nested collections");
×
981
        }
×
982
        if (auto mixed = dynamic_cast<Columns<Mixed>*>(subexpr.get())) {
4,152✔
983
            ok = true;
744✔
984
            mixed->path(indexes);
744✔
985
        }
744✔
986
        else if (auto dict = dynamic_cast<Columns<Dictionary>*>(subexpr.get())) {
3,408✔
987
            if (first_index.is_key()) {
256✔
988
                ok = true;
196✔
989
                auto trailing = first_index.get_key();
196✔
990
                if (trailing == "@values") {
196✔
991
                }
4✔
992
                else if (trailing == "@keys") {
192✔
993
                    subexpr = std::make_unique<ColumnDictionaryKeys>(*dict);
52✔
994
                }
52✔
995
                else {
140✔
996
                    dict->path(indexes);
140✔
997
                }
140✔
998
            }
196✔
999
            else if (first_index.is_all()) {
60✔
1000
                ok = true;
52✔
1001
                dict->path(indexes);
52✔
1002
            }
52✔
1003
        }
256✔
1004
        else if (auto coll = dynamic_cast<Columns<Lst<Mixed>>*>(subexpr.get())) {
3,152✔
1005
            ok = coll->indexes(indexes);
48✔
1006
        }
48✔
1007
        else if (auto coll = dynamic_cast<ColumnListBase*>(subexpr.get())) {
3,104✔
1008
            if (indexes.size() == 1) {
3,096✔
1009
                ok = coll->index(first_index);
3,096✔
1010
            }
3,096✔
1011
        }
3,096✔
1012

1013
        if (!ok) {
4,152✔
1014
            if (first_index.is_key()) {
1,760✔
1015
                auto trailing = first_index.get_key();
1,720✔
1016
                if (!post_op && is_length_suffix(trailing)) {
1,720✔
1017
                    // If 'length' is the operator, the last id in the path must be the name
1018
                    // of a list property
1019
                    path->path_elems.pop_back();
1,704✔
1020
                    const std::string& prop = path->path_elems.back().get_key();
1,704✔
1021
                    std::unique_ptr<Subexpr> subexpr{path->visit(drv, comp_type).column(prop, false)};
1,704✔
1022
                    if (auto list = dynamic_cast<ColumnListBase*>(subexpr.get())) {
1,704✔
1023
                        if (auto length_expr = list->get_element_length())
1,704✔
1024
                            return length_expr;
1,640✔
1025
                    }
1,704✔
1026
                }
1,704✔
1027
                throw InvalidQueryError(util::format("Property '%1.%2' has no property '%3'",
80✔
1028
                                                     m_link_chain.get_current_table()->get_class_name(), identifier,
80✔
1029
                                                     trailing));
80✔
1030
            }
1,720✔
1031
            else {
40✔
1032
                throw InvalidQueryError(util::format("Property '%1.%2' does not support index '%3'",
40✔
1033
                                                     m_link_chain.get_current_table()->get_class_name(), identifier,
40✔
1034
                                                     first_index));
40✔
1035
            }
40✔
1036
        }
1,760✔
1037
    }
4,152✔
1038
    if (post_op) {
477,160✔
1039
        return post_op->visit(drv, subexpr.get());
4,352✔
1040
    }
4,352✔
1041
    return subexpr;
472,808✔
1042
}
477,160✔
1043

1044
std::unique_ptr<Subexpr> SubqueryNode::visit(ParserDriver* drv, DataType)
1045
{
304✔
1046
    if (variable_name.size() < 2 || variable_name[0] != '$') {
304✔
1047
        throw SyntaxError(util::format("The subquery variable '%1' is invalid. The variable must start with "
4✔
1048
                                       "'$' and cannot be empty; for example '$x'.",
4✔
1049
                                       variable_name));
4✔
1050
    }
4✔
1051
    LinkChain lc = prop->path->visit(drv, prop->comp_type);
300✔
1052

1053
    ColKey col_key;
300✔
1054
    std::string identifier;
300✔
1055
    if (!prop->path->at_end()) {
300✔
1056
        identifier = prop->path->next_identifier();
8✔
1057
        col_key = lc.get_current_table()->get_column_key(identifier);
8✔
1058
    }
8✔
1059
    else {
292✔
1060
        identifier = prop->path->last_identifier();
292✔
1061
        col_key = lc.get_current_col();
292✔
1062
    }
292✔
1063

1064
    auto col_type = col_key.get_type();
300✔
1065
    if (col_key.is_list() && col_type != col_type_Link) {
300✔
1066
        throw InvalidQueryError(
4✔
1067
            util::format("A subquery can not operate on a list of primitive values (property '%1')", identifier));
4✔
1068
    }
4✔
1069
    // col_key.is_list => col_type == col_type_Link
1070
    if (!(col_key.is_list() || col_type == col_type_BackLink)) {
296✔
1071
        throw InvalidQueryError(util::format("A subquery must operate on a list property, but '%1' is type '%2'",
8✔
1072
                                             identifier, realm::get_data_type_name(DataType(col_type))));
8✔
1073
    }
8✔
1074

1075
    TableRef previous_table = drv->m_base_table;
288✔
1076
    drv->m_base_table = lc.get_current_table().cast_away_const();
288✔
1077
    bool did_add = drv->m_mapping.add_mapping(drv->m_base_table, variable_name, "");
288✔
1078
    if (!did_add) {
288✔
1079
        throw InvalidQueryError(util::format("Unable to create a subquery expression with variable '%1' since an "
4✔
1080
                                             "identical variable already exists in this context",
4✔
1081
                                             variable_name));
4✔
1082
    }
4✔
1083
    Query sub = subquery->visit(drv);
284✔
1084
    drv->m_mapping.remove_mapping(drv->m_base_table, variable_name);
284✔
1085
    drv->m_base_table = previous_table;
284✔
1086

1087
    return lc.subquery(sub);
284✔
1088
}
288✔
1089

1090
std::unique_ptr<Subexpr> PostOpNode::visit(ParserDriver*, Subexpr* subexpr)
1091
{
4,352✔
1092
    if (op_type == PostOpNode::SIZE) {
4,352✔
1093
        if (auto s = dynamic_cast<Columns<Link>*>(subexpr)) {
3,584✔
1094
            return s->count().clone();
344✔
1095
        }
344✔
1096
        if (auto s = dynamic_cast<ColumnListBase*>(subexpr)) {
3,240✔
1097
            return s->size().clone();
2,928✔
1098
        }
2,928✔
1099
        if (auto s = dynamic_cast<Columns<StringData>*>(subexpr)) {
312✔
1100
            return s->size().clone();
128✔
1101
        }
128✔
1102
        if (auto s = dynamic_cast<Columns<BinaryData>*>(subexpr)) {
184✔
1103
            return s->size().clone();
48✔
1104
        }
48✔
1105
        if (auto s = dynamic_cast<Columns<Mixed>*>(subexpr)) {
136✔
1106
            return s->size().clone();
124✔
1107
        }
124✔
1108
    }
136✔
1109
    else if (op_type == PostOpNode::TYPE) {
768✔
1110
        if (auto s = dynamic_cast<Columns<Mixed>*>(subexpr)) {
768✔
1111
            return s->type_of_value().clone();
636✔
1112
        }
636✔
1113
        if (auto s = dynamic_cast<ColumnsCollection<Mixed>*>(subexpr)) {
132✔
1114
            return s->type_of_value().clone();
112✔
1115
        }
112✔
1116
        if (auto s = dynamic_cast<ObjPropertyBase*>(subexpr)) {
20✔
1117
            return Value<TypeOfValue>(TypeOfValue(s->column_key())).clone();
8✔
1118
        }
8✔
1119
        if (dynamic_cast<Columns<Link>*>(subexpr)) {
12✔
1120
            return Value<TypeOfValue>(TypeOfValue(TypeOfValue::Attribute::ObjectLink)).clone();
12✔
1121
        }
12✔
1122
    }
12✔
1123

1124
    if (subexpr) {
12✔
1125
        throw InvalidQueryError(util::format("Operation '%1' is not supported on property of type '%2'", op_name,
12✔
1126
                                             get_data_type_name(DataType(subexpr->get_type()))));
12✔
1127
    }
12✔
1128
    REALM_UNREACHABLE();
1129
    return {};
×
1130
}
12✔
1131

1132
std::unique_ptr<Subexpr> LinkAggrNode::visit(ParserDriver* drv, DataType)
1133
{
848✔
1134
    auto subexpr = property->visit(drv);
848✔
1135
    auto link_prop = dynamic_cast<Columns<Link>*>(subexpr.get());
848✔
1136
    if (!link_prop) {
848✔
1137
        throw InvalidQueryError(util::format("Operation '%1' cannot apply to property '%2' because it is not a list",
40✔
1138
                                             agg_op_type_to_str(type), property->get_identifier()));
40✔
1139
    }
40✔
1140
    const LinkChain& link_chain = property->link_chain();
808✔
1141
    prop_name = drv->translate(link_chain, prop_name);
808✔
1142
    auto col_key = link_chain.get_current_table()->get_column_key(prop_name);
808✔
1143

1144
    switch (col_key.get_type()) {
808✔
1145
        case col_type_Int:
64✔
1146
            subexpr = link_prop->column<Int>(col_key).clone();
64✔
1147
            break;
64✔
1148
        case col_type_Float:
128✔
1149
            subexpr = link_prop->column<float>(col_key).clone();
128✔
1150
            break;
128✔
1151
        case col_type_Double:
344✔
1152
            subexpr = link_prop->column<double>(col_key).clone();
344✔
1153
            break;
344✔
1154
        case col_type_Decimal:
96✔
1155
            subexpr = link_prop->column<Decimal>(col_key).clone();
96✔
1156
            break;
96✔
1157
        case col_type_Timestamp:
56✔
1158
            subexpr = link_prop->column<Timestamp>(col_key).clone();
56✔
1159
            break;
56✔
1160
        case col_type_Mixed:
64✔
1161
            subexpr = link_prop->column<Mixed>(col_key).clone();
64✔
1162
            break;
64✔
1163
        default:
48✔
1164
            throw InvalidQueryError(util::format("collection aggregate not supported for type '%1'",
48✔
1165
                                                 get_data_type_name(DataType(col_key.get_type()))));
48✔
1166
    }
808✔
1167
    return aggregate(subexpr.get());
752✔
1168
}
808✔
1169

1170
std::unique_ptr<Subexpr> ListAggrNode::visit(ParserDriver* drv, DataType)
1171
{
2,744✔
1172
    auto subexpr = property->visit(drv);
2,744✔
1173
    return aggregate(subexpr.get());
2,744✔
1174
}
2,744✔
1175

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

1216
    return agg;
3,264✔
1217
}
3,496✔
1218

1219
void ConstantNode::decode_b64()
1220
{
1,432✔
1221
    const size_t encoded_size = text.size() - 5;
1,432✔
1222
    size_t buffer_size = util::base64_decoded_size(encoded_size);
1,432✔
1223
    m_decode_buffer.resize(buffer_size);
1,432✔
1224
    StringData window(text.c_str() + 4, encoded_size);
1,432✔
1225
    util::Optional<size_t> decoded_size = util::base64_decode(window, m_decode_buffer);
1,432✔
1226
    if (!decoded_size) {
1,432✔
1227
        throw SyntaxError("Invalid base64 value");
×
1228
    }
×
1229
    REALM_ASSERT_DEBUG_EX(*decoded_size <= encoded_size, *decoded_size, encoded_size);
1,432✔
1230
    m_decode_buffer.resize(*decoded_size); // truncate
1,432✔
1231
}
1,432✔
1232

1233
Mixed ConstantNode::get_value()
1234
{
42,770✔
1235
    switch (type) {
42,770✔
1236
        case Type::NUMBER:
24,696✔
1237
            return int64_t(strtoll(text.c_str(), nullptr, 0));
24,696✔
1238
        case Type::FLOAT:
2,152✔
1239
            if (text[text.size() - 1] == 'f') {
2,152✔
1240
                return strtof(text.c_str(), nullptr);
4✔
1241
            }
4✔
1242
            return strtod(text.c_str(), nullptr);
2,148✔
1243
        case Type::INFINITY_VAL: {
152✔
1244
            bool negative = text[0] == '-';
152✔
1245
            constexpr auto inf = std::numeric_limits<double>::infinity();
152✔
1246
            return negative ? -inf : inf;
152✔
1247
        }
2,152✔
1248
        case Type::NAN_VAL:
64✔
1249
            return type_punning<double>(0x7ff8000000000000);
64✔
1250
        case Type::STRING:
8,314✔
1251
            return StringData(text.data() + 1, text.size() - 2);
8,314✔
1252
        case Type::STRING_BASE64:
716✔
1253
            decode_b64();
716✔
1254
            return StringData(m_decode_buffer.data(), m_decode_buffer.size());
716✔
1255
        case Type::TIMESTAMP: {
620✔
1256
            auto s = text;
620✔
1257
            int64_t seconds;
620✔
1258
            int32_t nanoseconds;
620✔
1259
            if (s[0] == 'T') {
620✔
1260
                size_t colon_pos = s.find(":");
544✔
1261
                std::string s1 = s.substr(1, colon_pos - 1);
544✔
1262
                std::string s2 = s.substr(colon_pos + 1);
544✔
1263
                seconds = strtol(s1.c_str(), nullptr, 0);
544✔
1264
                nanoseconds = int32_t(strtol(s2.c_str(), nullptr, 0));
544✔
1265
            }
544✔
1266
            else {
76✔
1267
                // readable format YYYY-MM-DD-HH:MM:SS:NANOS nanos optional
1268
                struct tm tmp = tm();
76✔
1269
                char sep = s.find("@") < s.size() ? '@' : 'T';
76✔
1270
                std::string fmt = "%d-%d-%d"s + sep + "%d:%d:%d:%d"s;
76✔
1271
                int cnt = sscanf(s.c_str(), fmt.c_str(), &tmp.tm_year, &tmp.tm_mon, &tmp.tm_mday, &tmp.tm_hour,
76✔
1272
                                 &tmp.tm_min, &tmp.tm_sec, &nanoseconds);
76✔
1273
                REALM_ASSERT(cnt >= 6);
76✔
1274
                tmp.tm_year -= 1900; // epoch offset (see man mktime)
76✔
1275
                tmp.tm_mon -= 1;     // converts from 1-12 to 0-11
76✔
1276

1277
                if (tmp.tm_year < 0) {
76✔
1278
                    // platform timegm functions do not throw errors, they return -1 which is also a valid time
1279
                    throw InvalidQueryError("Conversion of dates before 1900 is not supported.");
16✔
1280
                }
16✔
1281

1282
                seconds = platform_timegm(tmp); // UTC time
60✔
1283
                if (cnt == 6) {
60✔
1284
                    nanoseconds = 0;
32✔
1285
                }
32✔
1286
                if (nanoseconds < 0) {
60✔
1287
                    throw SyntaxError("The nanoseconds of a Timestamp cannot be negative.");
×
1288
                }
×
1289
                if (seconds < 0) { // seconds determines the sign of the nanoseconds part
60✔
1290
                    nanoseconds *= -1;
24✔
1291
                }
24✔
1292
            }
60✔
1293
            return get_timestamp_if_valid(seconds, nanoseconds);
604✔
1294
        }
620✔
1295
        case Type::UUID_T:
672✔
1296
            return UUID(text.substr(5, text.size() - 6));
672✔
1297
        case Type::OID:
772✔
1298
            return ObjectId(text.substr(4, text.size() - 5).c_str());
772✔
1299
        case Type::LINK:
8✔
1300
            return ObjKey(strtol(text.substr(1, text.size() - 1).c_str(), nullptr, 0));
8✔
1301
        case Type::TYPED_LINK: {
48✔
1302
            size_t colon_pos = text.find(":");
48✔
1303
            auto table_key_val = uint32_t(strtol(text.substr(1, colon_pos - 1).c_str(), nullptr, 0));
48✔
1304
            auto obj_key_val = strtol(text.substr(colon_pos + 1).c_str(), nullptr, 0);
48✔
1305
            return ObjLink(TableKey(table_key_val), ObjKey(obj_key_val));
48✔
1306
        }
620✔
1307
        case Type::NULL_VAL:
2,300✔
1308
            return {};
2,300✔
1309
        case Type::TRUE:
160✔
1310
            return {true};
160✔
1311
        case Type::FALSE:
312✔
1312
            return {false};
312✔
1313
        case Type::ARG:
✔
1314
            break;
×
1315
        case BINARY_STR: {
1,068✔
1316
            return BinaryData(text.data() + 1, text.size() - 2);
1,068✔
1317
        }
620✔
1318
        case BINARY_BASE64:
716✔
1319
            decode_b64();
716✔
1320
            return BinaryData(m_decode_buffer.data(), m_decode_buffer.size());
716✔
1321
    }
42,770✔
1322
    return {};
×
1323
}
42,770✔
1324

1325
std::unique_ptr<Subexpr> ConstantNode::visit(ParserDriver* drv, DataType hint)
1326
{
475,932✔
1327
    std::unique_ptr<Subexpr> ret;
475,932✔
1328
    std::string explain_value_message = text;
475,932✔
1329
    Mixed value;
475,932✔
1330

1331
    auto convert_if_needed = [&](Mixed& value) -> void {
475,932✔
1332
        switch (value.get_type()) {
473,534✔
1333
            case type_Int:
25,696✔
1334
                if (hint == type_Decimal) {
25,696✔
1335
                    value = Decimal128(value.get_int());
1,296✔
1336
                }
1,296✔
1337
                break;
25,696✔
1338
            case type_Double: {
2,944✔
1339
                auto double_val = value.get_double();
2,944✔
1340
                if (std::isinf(double_val) && (!Mixed::is_numeric(hint) || hint == type_Int)) {
2,944✔
1341
                    throw InvalidQueryError(util::format("Infinity not supported for %1", get_data_type_name(hint)));
8✔
1342
                }
8✔
1343

1344
                switch (hint) {
2,936✔
1345
                    case type_Float:
184✔
1346
                        value = float(double_val);
184✔
1347
                        break;
184✔
1348
                    case type_Decimal:
480✔
1349
                        // If not argument, try decode again to get full precision
1350
                        value = (type == Type::ARG) ? Decimal128(double_val) : Decimal128(text);
480✔
1351
                        break;
480✔
1352
                    case type_Int: {
80✔
1353
                        int64_t int_val = int64_t(double_val);
80✔
1354
                        // Only return an integer if it precisely represents val
1355
                        if (double(int_val) == double_val) {
80✔
1356
                            value = int_val;
24✔
1357
                        }
24✔
1358
                        break;
80✔
1359
                    }
×
1360
                    default:
2,192✔
1361
                        break;
2,192✔
1362
                }
2,936✔
1363
                break;
2,936✔
1364
            }
2,936✔
1365
            case type_Float: {
2,936✔
1366
                if (hint == type_Int) {
492✔
1367
                    float float_val = value.get_float();
52✔
1368
                    if (std::isinf(float_val)) {
52✔
1369
                        throw InvalidQueryError(
8✔
1370
                            util::format("Infinity not supported for %1", get_data_type_name(hint)));
8✔
1371
                    }
8✔
1372
                    if (std::isnan(float_val)) {
44✔
1373
                        throw InvalidQueryError(util::format("NaN not supported for %1", get_data_type_name(hint)));
8✔
1374
                    }
8✔
1375
                    int64_t int_val = int64_t(float_val);
36✔
1376
                    if (float(int_val) == float_val) {
36✔
1377
                        value = int_val;
16✔
1378
                    }
16✔
1379
                }
36✔
1380
                break;
476✔
1381
            }
492✔
1382
            case type_String: {
9,480✔
1383
                StringData str = value.get_string();
9,480✔
1384
                switch (hint) {
9,480✔
1385
                    case type_Int:
420✔
1386
                        if (auto val = string_to<int64_t>(str)) {
420✔
1387
                            value = *val;
404✔
1388
                        }
404✔
1389
                        break;
420✔
1390
                    case type_Float:
8✔
1391
                        if (auto val = string_to<float>(str)) {
8✔
1392
                            value = *val;
×
1393
                        }
×
1394
                        break;
8✔
1395
                    case type_Double:
8✔
1396
                        if (auto val = string_to<double>(str)) {
8✔
1397
                            value = *val;
×
1398
                        }
×
1399
                        break;
8✔
1400
                    case type_Decimal:
4✔
1401
                        if (auto val = string_to<Decimal128>(str)) {
4✔
1402
                            value = *val;
×
1403
                        }
×
1404
                        break;
4✔
1405
                    default:
9,038✔
1406
                        break;
9,038✔
1407
                }
9,480✔
1408
                break;
9,478✔
1409
            }
9,480✔
1410
            default:
434,924✔
1411
                break;
434,924✔
1412
        }
473,534✔
1413
    };
473,534✔
1414

1415
    if (type == Type::ARG) {
475,932✔
1416
        size_t arg_no = size_t(strtol(text.substr(1).c_str(), nullptr, 10));
433,162✔
1417
        if (m_comp_type && !drv->m_args.is_argument_list(arg_no)) {
433,162✔
1418
            throw InvalidQueryError(util::format(
12✔
1419
                "ANY/ALL/NONE are only allowed on arguments which contain a list but '%1' is not a list.",
12✔
1420
                explain_value_message));
12✔
1421
        }
12✔
1422
        if (drv->m_args.is_argument_list(arg_no)) {
433,150✔
1423
            std::vector<Mixed> mixed_list = drv->m_args.list_for_argument(arg_no);
160✔
1424
            for (auto& mixed : mixed_list) {
408✔
1425
                if (!mixed.is_null()) {
408✔
1426
                    convert_if_needed(mixed);
396✔
1427
                }
396✔
1428
            }
408✔
1429
            return copy_list_of_args(mixed_list);
160✔
1430
        }
160✔
1431
        if (drv->m_args.is_argument_null(arg_no)) {
432,990✔
1432
            explain_value_message = util::format("argument '%1' which is NULL", explain_value_message);
256✔
1433
        }
256✔
1434
        else {
432,734✔
1435
            value = drv->m_args.mixed_for_argument(arg_no);
432,734✔
1436
            if (value.is_null()) {
432,734✔
1437
                explain_value_message = util::format("argument %1 of type null", explain_value_message);
4✔
1438
            }
4✔
1439
            else if (value.is_type(type_TypedLink)) {
432,730✔
1440
                explain_value_message =
36✔
1441
                    util::format("%1 which links to %2", explain_value_message,
36✔
1442
                                 print_pretty_objlink(value.get<ObjLink>(), drv->m_base_table->get_parent_group()));
36✔
1443
            }
36✔
1444
            else {
432,694✔
1445
                explain_value_message = util::format("argument %1 with value '%2'", explain_value_message, value);
432,694✔
1446
            }
432,694✔
1447
        }
432,734✔
1448
    }
432,990✔
1449
    else {
42,770✔
1450
        value = get_value();
42,770✔
1451
    }
42,770✔
1452

1453
    if (m_target_table) {
475,760✔
1454
        // There is a table name set. This must be an ObjLink
1455
        const Group* g = drv->m_base_table->get_parent_group();
112✔
1456
        auto table = g->get_table(m_target_table);
112✔
1457
        if (!table) {
112✔
1458
            // Perhaps class prefix is missing
1459
            Group::TableNameBuffer buffer;
8✔
1460
            table = g->get_table(Group::class_name_to_table_name(m_target_table, buffer));
8✔
1461
        }
8✔
1462
        if (!table) {
112✔
1463
            throw InvalidQueryError(util::format("Unknown object type '%1'", m_target_table));
×
1464
        }
×
1465
        auto obj_key = table->find_primary_key(value);
112✔
1466
        value = ObjLink(table->get_key(), ObjKey(obj_key));
112✔
1467
    }
112✔
1468

1469
    if (value.is_null()) {
475,760✔
1470
        if (hint == type_String) {
2,560✔
1471
            return std::make_unique<ConstantStringValue>(StringData()); // Null string
348✔
1472
        }
348✔
1473
        else if (hint == type_Binary) {
2,212✔
1474
            return std::make_unique<Value<Binary>>(BinaryData()); // Null string
320✔
1475
        }
320✔
1476
        else {
1,892✔
1477
            return std::make_unique<Value<null>>(realm::null());
1,892✔
1478
        }
1,892✔
1479
    }
2,560✔
1480

1481
    convert_if_needed(value);
473,200✔
1482

1483
    if (type == Type::ARG && !(m_target_table || Mixed::data_types_are_comparable(value.get_type(), hint) ||
473,200✔
1484
                               (value.is_type(type_TypedLink) && hint == type_Link) ||
432,684✔
1485
                               (value.is_type(type_String) && hint == type_TypeOfValue))) {
432,684✔
1486
        throw InvalidQueryArgError(
176✔
1487
            util::format("Cannot compare %1 to a %2", explain_value_message, get_data_type_name(hint)));
176✔
1488
    }
176✔
1489

1490
    switch (value.get_type()) {
473,024✔
1491
        case type_Int: {
24,618✔
1492
            ret = std::make_unique<Value<int64_t>>(value.get_int());
24,618✔
1493
            break;
24,618✔
1494
        }
×
1495
        case type_Float: {
616✔
1496
            ret = std::make_unique<Value<float>>(value.get_float());
616✔
1497
            break;
616✔
1498
        }
×
1499
        case type_Decimal:
2,264✔
1500
            ret = std::make_unique<Value<Decimal128>>(value.get_decimal());
2,264✔
1501
            break;
2,264✔
1502
        case type_Double: {
2,204✔
1503
            ret = std::make_unique<Value<double>>(value.get_double());
2,204✔
1504
            break;
2,204✔
1505
        }
×
1506
        case type_String: {
8,894✔
1507
            StringData str = value.get_string();
8,894✔
1508
            if (hint == type_TypeOfValue) {
8,894✔
1509
                TypeOfValue type_of_value(std::string_view(str.data(), str.size()));
680✔
1510
                ret = std::make_unique<Value<TypeOfValue>>(type_of_value);
680✔
1511
            }
680✔
1512
            else {
8,214✔
1513
                ret = std::make_unique<ConstantStringValue>(str);
8,214✔
1514
            }
8,214✔
1515
            break;
8,894✔
1516
        }
×
1517
        case type_Timestamp:
956✔
1518
            ret = std::make_unique<Value<Timestamp>>(value.get_timestamp());
956✔
1519
            break;
956✔
1520
        case type_UUID:
1,064✔
1521
            ret = std::make_unique<Value<UUID>>(value.get_uuid());
1,064✔
1522
            break;
1,064✔
1523
        case type_ObjectId:
429,390✔
1524
            ret = std::make_unique<Value<ObjectId>>(value.get_object_id());
429,390✔
1525
            break;
429,390✔
1526
        case type_Link:
20✔
1527
            ret = std::make_unique<Value<ObjKey>>(value.get<ObjKey>());
20✔
1528
            break;
20✔
1529
        case type_TypedLink:
196✔
1530
            ret = std::make_unique<Value<ObjLink>>(value.get<ObjLink>());
196✔
1531
            break;
196✔
1532
        case type_Bool:
776✔
1533
            ret = std::make_unique<Value<Bool>>(value.get_bool());
776✔
1534
            break;
776✔
1535
        case type_Binary:
1,936✔
1536
            ret = std::make_unique<ConstantBinaryValue>(value.get_binary());
1,936✔
1537
            break;
1,936✔
1538
        case type_Mixed:
✔
1539
            break;
×
1540
    }
473,024✔
1541
    if (!ret) {
472,912✔
1542
        throw InvalidQueryError(
×
1543
            util::format("Unsupported comparison between property of type '%1' and constant value: %2",
×
1544
                         get_data_type_name(hint), explain_value_message));
×
1545
    }
×
1546
    return ret;
472,912✔
1547
}
472,912✔
1548

1549
std::unique_ptr<ConstantMixedList> ConstantNode::copy_list_of_args(std::vector<Mixed>& mixed_args)
1550
{
160✔
1551
    std::unique_ptr<ConstantMixedList> args_in_list = std::make_unique<ConstantMixedList>(mixed_args.size());
160✔
1552
    size_t ndx = 0;
160✔
1553
    for (const auto& mixed : mixed_args) {
408✔
1554
        args_in_list->set(ndx++, mixed);
408✔
1555
    }
408✔
1556
    if (m_comp_type) {
160✔
1557
        args_in_list->set_comparison_type(*m_comp_type);
20✔
1558
    }
20✔
1559
    return args_in_list;
160✔
1560
}
160✔
1561

1562
Mixed Arguments::mixed_for_argument(size_t arg_no)
1563
{
3,988✔
1564
    switch (type_for_argument(arg_no)) {
3,988✔
1565
        case type_Int:
464✔
1566
            return int64_t(long_for_argument(arg_no));
464✔
1567
        case type_String:
380✔
1568
            return string_for_argument(arg_no);
380✔
1569
        case type_Binary:
176✔
1570
            return binary_for_argument(arg_no);
176✔
1571
        case type_Bool:
316✔
1572
            return bool_for_argument(arg_no);
316✔
1573
        case type_Float:
436✔
1574
            return float_for_argument(arg_no);
436✔
1575
        case type_Double:
440✔
1576
            return double_for_argument(arg_no);
440✔
1577
        case type_Timestamp:
392✔
1578
            try {
392✔
1579
                return timestamp_for_argument(arg_no);
392✔
1580
            }
392✔
1581
            catch (const std::exception&) {
392✔
1582
            }
×
1583
            return objectid_for_argument(arg_no);
×
1584
        case type_ObjectId:
524✔
1585
            try {
524✔
1586
                return objectid_for_argument(arg_no);
524✔
1587
            }
524✔
1588
            catch (const std::exception&) {
524✔
1589
            }
×
1590
            return timestamp_for_argument(arg_no);
×
1591
        case type_Decimal:
420✔
1592
            return decimal128_for_argument(arg_no);
420✔
1593
        case type_UUID:
388✔
1594
            return uuid_for_argument(arg_no);
388✔
1595
        case type_Link:
12✔
1596
            return object_index_for_argument(arg_no);
12✔
1597
        case type_TypedLink:
40✔
1598
            return objlink_for_argument(arg_no);
40✔
1599
        default:
✔
1600
            break;
×
1601
    }
3,988✔
1602
    return {};
×
1603
}
3,988✔
1604

1605
#if REALM_ENABLE_GEOSPATIAL
1606
GeospatialNode::GeospatialNode(GeospatialNode::Box, GeoPoint& p1, GeoPoint& p2)
1607
    : m_geo{Geospatial{GeoBox{p1, p2}}}
10✔
1608
{
20✔
1609
}
20✔
1610

1611
GeospatialNode::GeospatialNode(Circle, GeoPoint& p, double radius)
1612
    : m_geo{Geospatial{GeoCircle{radius, p}}}
30✔
1613
{
60✔
1614
}
60✔
1615

1616
GeospatialNode::GeospatialNode(Polygon, GeoPoint& p)
1617
    : m_points({{p}})
1618
{
×
1619
}
×
1620

1621
GeospatialNode::GeospatialNode(Loop, GeoPoint& p)
1622
    : m_points({{p}})
34✔
1623
{
68✔
1624
}
68✔
1625

1626
void GeospatialNode::add_point_to_loop(GeoPoint& p)
1627
{
272✔
1628
    m_points.back().push_back(p);
272✔
1629
}
272✔
1630

1631
void GeospatialNode::add_loop_to_polygon(GeospatialNode* node)
1632
{
8✔
1633
    m_points.push_back(node->m_points.back());
8✔
1634
}
8✔
1635

1636
std::unique_ptr<Subexpr> GeospatialNode::visit(ParserDriver*, DataType)
1637
{
124✔
1638
    std::unique_ptr<Subexpr> ret;
124✔
1639
    if (m_geo.get_type() != Geospatial::Type::Invalid) {
124✔
1640
        ret = std::make_unique<ConstantGeospatialValue>(m_geo);
72✔
1641
    }
72✔
1642
    else {
52✔
1643
        ret = std::make_unique<ConstantGeospatialValue>(GeoPolygon{m_points});
52✔
1644
    }
52✔
1645
    return ret;
124✔
1646
}
124✔
1647
#endif
1648

1649
std::unique_ptr<Subexpr> ListNode::visit(ParserDriver* drv, DataType hint)
1650
{
2,448✔
1651
    if (hint == type_TypeOfValue) {
2,448✔
1652
        try {
12✔
1653
            std::unique_ptr<Value<TypeOfValue>> ret = std::make_unique<Value<TypeOfValue>>();
12✔
1654
            constexpr bool is_list = true;
12✔
1655
            ret->init(is_list, elements.size());
12✔
1656
            ret->set_comparison_type(m_comp_type);
12✔
1657
            size_t ndx = 0;
12✔
1658
            for (auto constant : elements) {
24✔
1659
                std::unique_ptr<Subexpr> evaluated = constant->visit(drv, hint);
24✔
1660
                if (auto converted = dynamic_cast<Value<TypeOfValue>*>(evaluated.get())) {
24✔
1661
                    ret->set(ndx++, converted->get(0));
20✔
1662
                }
20✔
1663
                else {
4✔
1664
                    throw InvalidQueryError(util::format("Invalid constant inside constant list: %1",
4✔
1665
                                                         evaluated->description(drv->m_serializer_state)));
4✔
1666
                }
4✔
1667
            }
24✔
1668
            return ret;
8✔
1669
        }
12✔
1670
        catch (const std::runtime_error& e) {
12✔
1671
            throw InvalidQueryArgError(e.what());
×
1672
        }
×
1673
    }
12✔
1674

1675
    auto ret = std::make_unique<ConstantMixedList>(elements.size());
2,436✔
1676
    ret->set_comparison_type(m_comp_type);
2,436✔
1677
    size_t ndx = 0;
2,436✔
1678
    for (auto constant : elements) {
4,772✔
1679
        auto evaulated_constant = constant->visit(drv, hint);
4,772✔
1680
        if (auto value = dynamic_cast<const ValueBase*>(evaulated_constant.get())) {
4,772✔
1681
            REALM_ASSERT_EX(value->size() == 1, value->size());
4,772✔
1682
            ret->set(ndx++, value->get(0));
4,772✔
1683
        }
4,772✔
1684
        else {
×
1685
            throw InvalidQueryError("Invalid constant inside constant list");
×
1686
        }
×
1687
    }
4,772✔
1688
    return ret;
2,436✔
1689
}
2,436✔
1690

1691
void PathNode::resolve_arg(ParserDriver* drv)
1692
{
479,230✔
1693
    if (arg.size()) {
479,230✔
1694
        if (path_elems.size()) {
32✔
1695
            throw InvalidQueryError("Key path argument cannot be mixed with other elements");
×
1696
        }
×
1697
        auto arg_str = drv->get_arg_for_key_path(arg);
32✔
1698
        const char* path = arg_str.data();
32✔
1699
        do {
44✔
1700
            auto p = find_chr(path, '.');
44✔
1701
            StringData elem(path, p - path);
44✔
1702
            add_element(elem);
44✔
1703
            path = p;
44✔
1704
        } while (*path++ == '.');
44✔
1705
    }
32✔
1706
}
479,230✔
1707

1708
LinkChain PathNode::visit(ParserDriver* drv, util::Optional<ExpressionComparisonType> comp_type)
1709
{
481,222✔
1710
    LinkChain link_chain(drv->m_base_table, comp_type);
481,222✔
1711
    for (current_path_elem = path_elems.begin(); current_path_elem != path_elems.end(); ++current_path_elem) {
498,268✔
1712
        if (current_path_elem->is_key()) {
495,864✔
1713
            const std::string& raw_path_elem = current_path_elem->get_key();
495,774✔
1714
            auto path_elem = drv->translate(link_chain, raw_path_elem);
495,774✔
1715
            if (path_elem.find("@links.") == 0) {
495,774✔
1716
                std::string_view table_column_pair(path_elem);
504✔
1717
                table_column_pair = table_column_pair.substr(7);
504✔
1718
                auto dot_pos = table_column_pair.find('.');
504✔
1719
                auto table_name = table_column_pair.substr(0, dot_pos);
504✔
1720
                auto column_name = table_column_pair.substr(dot_pos + 1);
504✔
1721
                drv->backlink(link_chain, table_name, column_name);
504✔
1722
                continue;
504✔
1723
            }
504✔
1724
            if (path_elem == "@values") {
495,270✔
1725
                if (!link_chain.get_current_col().is_dictionary()) {
24✔
1726
                    throw InvalidQueryError("@values only allowed on dictionaries");
×
1727
                }
×
1728
                continue;
24✔
1729
            }
24✔
1730
            if (path_elem.empty()) {
495,246✔
1731
                continue; // this element has been removed, this happens in subqueries
284✔
1732
            }
284✔
1733

1734
            // Check if it is a link
1735
            if (link_chain.link(path_elem)) {
494,962✔
1736
                continue;
16,096✔
1737
            }
16,096✔
1738
            // The next identifier being a property on the linked to object takes precedence
1739
            if (link_chain.get_current_table()->get_column_key(path_elem)) {
478,866✔
1740
                break;
478,686✔
1741
            }
478,686✔
1742
        }
478,866✔
1743
        if (!link_chain.index(*current_path_elem))
270✔
1744
            break;
132✔
1745
    }
270✔
1746
    return link_chain;
481,222✔
1747
}
481,222✔
1748

1749
DescriptorNode::~DescriptorNode() {}
1,404✔
1750

1751
DescriptorOrderingNode::~DescriptorOrderingNode() {}
46,238✔
1752

1753
std::unique_ptr<DescriptorOrdering> DescriptorOrderingNode::visit(ParserDriver* drv)
1754
{
43,828✔
1755
    auto target = drv->m_base_table;
43,828✔
1756
    std::unique_ptr<DescriptorOrdering> ordering;
43,828✔
1757
    for (auto cur_ordering : orderings) {
43,828✔
1758
        if (!ordering)
1,140✔
1759
            ordering = std::make_unique<DescriptorOrdering>();
708✔
1760
        if (cur_ordering->get_type() == DescriptorNode::LIMIT) {
1,140✔
1761
            ordering->append_limit(LimitDescriptor(cur_ordering->limit));
340✔
1762
        }
340✔
1763
        else {
800✔
1764
            bool is_distinct = cur_ordering->get_type() == DescriptorNode::DISTINCT;
800✔
1765
            std::vector<std::vector<ExtendedColumnKey>> property_columns;
800✔
1766
            for (Path& path : cur_ordering->columns) {
836✔
1767
                std::vector<ExtendedColumnKey> columns;
836✔
1768
                LinkChain link_chain(target);
836✔
1769
                ColKey col_key;
836✔
1770
                for (size_t ndx_in_path = 0; ndx_in_path < path.size(); ++ndx_in_path) {
1,780✔
1771
                    std::string prop_name = drv->translate(link_chain, path[ndx_in_path].get_key());
952✔
1772
                    // If last column was a dictionary, We will treat the next entry as a key to
1773
                    // the dictionary
1774
                    if (col_key && col_key.is_dictionary()) {
952✔
1775
                        columns.back().set_index(prop_name);
32✔
1776
                    }
32✔
1777
                    else {
920✔
1778
                        col_key = link_chain.get_current_table()->get_column_key(prop_name);
920✔
1779
                        if (!col_key) {
920✔
1780
                            throw InvalidQueryError(util::format(
8✔
1781
                                "No property '%1' found on object type '%2' specified in '%3' clause", prop_name,
8✔
1782
                                link_chain.get_current_table()->get_class_name(), is_distinct ? "distinct" : "sort"));
8✔
1783
                        }
8✔
1784
                        columns.emplace_back(col_key);
912✔
1785
                        if (ndx_in_path < path.size() - 1) {
912✔
1786
                            link_chain.link(col_key);
116✔
1787
                        }
116✔
1788
                    }
912✔
1789
                }
952✔
1790
                property_columns.push_back(columns);
828✔
1791
            }
828✔
1792

1793
            if (is_distinct) {
792✔
1794
                ordering->append_distinct(DistinctDescriptor(property_columns));
252✔
1795
            }
252✔
1796
            else {
540✔
1797
                ordering->append_sort(SortDescriptor(property_columns, cur_ordering->ascending),
540✔
1798
                                      SortDescriptor::MergeMode::prepend);
540✔
1799
            }
540✔
1800
        }
792✔
1801
    }
1,140✔
1802

1803
    return ordering;
43,820✔
1804
}
43,828✔
1805

1806
// If one of the expresions is constant, it should be right
1807
static void verify_conditions(Subexpr* left, Subexpr* right, util::serializer::SerialisationState& state)
1808
{
474,908✔
1809
    if (dynamic_cast<ColumnListBase*>(left) && dynamic_cast<ColumnListBase*>(right)) {
474,908✔
1810
        throw InvalidQueryError(
32✔
1811
            util::format("Ordered comparison between two primitive lists is not implemented yet ('%1' and '%2')",
32✔
1812
                         left->description(state), right->description(state)));
32✔
1813
    }
32✔
1814
    if (dynamic_cast<Value<TypeOfValue>*>(left) && dynamic_cast<Value<TypeOfValue>*>(right)) {
474,876✔
1815
        throw InvalidQueryError(util::format("Comparison between two constants is not supported ('%1' and '%2')",
8✔
1816
                                             left->description(state), right->description(state)));
8✔
1817
    }
8✔
1818
    if (auto link_column = dynamic_cast<Columns<Link>*>(left)) {
474,868✔
1819
        if (link_column->has_multiple_values() && right->has_single_value() && right->get_mixed().is_null()) {
428✔
1820
            throw InvalidQueryError(
20✔
1821
                util::format("Cannot compare linklist ('%1') with NULL", left->description(state)));
20✔
1822
        }
20✔
1823
    }
428✔
1824
}
474,868✔
1825

1826
ParserDriver::ParserDriver(TableRef t, Arguments& args, const query_parser::KeyPathMapping& mapping)
1827
    : m_base_table(t)
23,350✔
1828
    , m_args(args)
23,350✔
1829
    , m_mapping(mapping)
23,350✔
1830
{
46,700✔
1831
    yylex_init(&m_yyscanner);
46,700✔
1832
}
46,700✔
1833

1834
ParserDriver::~ParserDriver()
1835
{
46,700✔
1836
    yylex_destroy(m_yyscanner);
46,700✔
1837
}
46,700✔
1838

1839
PathElement ParserDriver::get_arg_for_index(const std::string& i)
1840
{
4✔
1841
    REALM_ASSERT(i[0] == '$');
4✔
1842
    size_t arg_no = size_t(strtol(i.substr(1).c_str(), nullptr, 10));
4✔
1843
    if (m_args.is_argument_null(arg_no) || m_args.is_argument_list(arg_no)) {
4✔
1844
        throw InvalidQueryError("Invalid index parameter");
×
1845
    }
×
1846
    auto type = m_args.type_for_argument(arg_no);
4✔
1847
    switch (type) {
4✔
1848
        case type_Int:
✔
1849
            return size_t(m_args.long_for_argument(arg_no));
×
1850
        case type_String:
4✔
1851
            return m_args.string_for_argument(arg_no);
4✔
1852
        default:
✔
1853
            throw InvalidQueryError("Invalid index type");
×
1854
    }
4✔
1855
}
4✔
1856

1857
std::string ParserDriver::get_arg_for_key_path(const std::string& i)
1858
{
32✔
1859
    REALM_ASSERT(i[0] == '$');
32✔
1860
    REALM_ASSERT(i[1] == 'K');
32✔
1861
    size_t arg_no = size_t(strtol(i.substr(2).c_str(), nullptr, 10));
32✔
1862
    if (m_args.is_argument_null(arg_no) || m_args.is_argument_list(arg_no)) {
32✔
1863
        throw InvalidQueryArgError(util::format("Null or list cannot be used for parameter '%1'", i));
4✔
1864
    }
4✔
1865
    auto type = m_args.type_for_argument(arg_no);
28✔
1866
    if (type != type_String) {
28✔
1867
        throw InvalidQueryArgError(util::format("Invalid index type for '%1'. Expected a string, but found type '%2'",
4✔
1868
                                                i, get_data_type_name(type)));
4✔
1869
    }
4✔
1870
    return m_args.string_for_argument(arg_no);
24✔
1871
}
28✔
1872

1873
double ParserDriver::get_arg_for_coordinate(const std::string& str)
1874
{
80✔
1875
    REALM_ASSERT(str[0] == '$');
80✔
1876
    size_t arg_no = size_t(strtol(str.substr(1).c_str(), nullptr, 10));
80✔
1877
    if (m_args.is_argument_null(arg_no)) {
80✔
1878
        throw InvalidQueryError(util::format("NULL cannot be used in coordinate at argument '%1'", str));
4✔
1879
    }
4✔
1880
    if (m_args.is_argument_list(arg_no)) {
76✔
1881
        throw InvalidQueryError(util::format("A list cannot be used in a coordinate at argument '%1'", str));
×
1882
    }
×
1883

1884
    auto type = m_args.type_for_argument(arg_no);
76✔
1885
    switch (type) {
76✔
1886
        case type_Int:
✔
1887
            return double(m_args.long_for_argument(arg_no));
×
1888
        case type_Double:
68✔
1889
            return m_args.double_for_argument(arg_no);
68✔
1890
        case type_Float:
✔
1891
            return double(m_args.float_for_argument(arg_no));
×
1892
        default:
8✔
1893
            throw InvalidQueryError(util::format("Invalid parameter '%1' used in coordinate at argument '%2'",
8✔
1894
                                                 get_data_type_name(type), str));
8✔
1895
    }
76✔
1896
}
76✔
1897

1898
auto ParserDriver::cmp(const std::vector<ExpressionNode*>& values) -> std::pair<SubexprPtr, SubexprPtr>
1899
{
475,902✔
1900
    SubexprPtr left;
475,902✔
1901
    SubexprPtr right;
475,902✔
1902

1903
    auto left_is_constant = values[0]->is_constant();
475,902✔
1904
    auto right_is_constant = values[1]->is_constant();
475,902✔
1905

1906
    if (left_is_constant && right_is_constant) {
475,902✔
1907
        throw InvalidQueryError("Cannot compare two constants");
×
1908
    }
×
1909

1910
    if (right_is_constant) {
475,902✔
1911
        // Take left first - it cannot be a constant
1912
        left = values[0]->visit(this);
471,798✔
1913
        right = values[1]->visit(this, left->get_type());
471,798✔
1914
        verify_conditions(left.get(), right.get(), m_serializer_state);
471,798✔
1915
    }
471,798✔
1916
    else {
4,104✔
1917
        right = values[1]->visit(this);
4,104✔
1918
        if (left_is_constant) {
4,104✔
1919
            left = values[0]->visit(this, right->get_type());
1,492✔
1920
        }
1,492✔
1921
        else {
2,612✔
1922
            left = values[0]->visit(this);
2,612✔
1923
        }
2,612✔
1924
        verify_conditions(right.get(), left.get(), m_serializer_state);
4,104✔
1925
    }
4,104✔
1926
    return {std::move(left), std::move(right)};
475,902✔
1927
}
475,902✔
1928

1929
auto ParserDriver::column(LinkChain& link_chain, PathNode* path) -> SubexprPtr
1930
{
478,896✔
1931
    if (path->at_end()) {
478,896✔
1932
        // This is a link property. We can optimize by usingColumns<Link>.
1933
        // However Columns<Link> does not handle @keys and indexes
1934
        auto extended_col_key = link_chain.m_link_cols.back();
1,796✔
1935
        if (!extended_col_key.has_index()) {
1,796✔
1936
            return link_chain.create_subexpr<Link>(ColKey(extended_col_key));
1,792✔
1937
        }
1,792✔
1938
        link_chain.pop_back();
4✔
1939
        --path->current_path_elem;
4✔
1940
        --path->current_path_elem;
4✔
1941
    }
4✔
1942
    auto identifier = m_mapping.translate(link_chain, path->next_identifier());
477,104✔
1943
    if (auto col = link_chain.column(identifier, !path->at_end())) {
477,104✔
1944
        return col;
476,940✔
1945
    }
476,940✔
1946
    throw InvalidQueryError(
164✔
1947
        util::format("'%1' has no property '%2'", link_chain.get_current_table()->get_class_name(), identifier));
164✔
1948
}
477,104✔
1949

1950
void ParserDriver::backlink(LinkChain& link_chain, std::string_view raw_table_name, std::string_view raw_column_name)
1951
{
504✔
1952
    std::string table_name = m_mapping.translate_table_name(raw_table_name);
504✔
1953
    auto origin_table = m_base_table->get_parent_group()->get_table(table_name);
504✔
1954
    ColKey origin_column;
504✔
1955
    std::string column_name{raw_column_name};
504✔
1956
    if (origin_table) {
504✔
1957
        column_name = m_mapping.translate(origin_table, column_name);
496✔
1958
        origin_column = origin_table->get_column_key(column_name);
496✔
1959
    }
496✔
1960
    if (!origin_column) {
504✔
1961
        auto origin_table_name = Group::table_name_to_class_name(table_name);
12✔
1962
        auto current_table_name = link_chain.get_current_table()->get_class_name();
12✔
1963
        throw InvalidQueryError(util::format("No property '%1' found in type '%2' which links to type '%3'",
12✔
1964
                                             column_name, origin_table_name, current_table_name));
12✔
1965
    }
12✔
1966
    link_chain.backlink(*origin_table, origin_column);
492✔
1967
}
492✔
1968

1969
std::string ParserDriver::translate(const LinkChain& link_chain, const std::string& identifier)
1970
{
497,538✔
1971
    return m_mapping.translate(link_chain, identifier);
497,538✔
1972
}
497,538✔
1973

1974
int ParserDriver::parse(const std::string& str)
1975
{
46,696✔
1976
    // std::cout << str << std::endl;
1977
    parse_buffer.append(str);
46,696✔
1978
    parse_buffer.append("\0\0", 2); // Flex requires 2 terminating zeroes
46,696✔
1979
    scan_begin(m_yyscanner, trace_scanning);
46,696✔
1980
    yy::parser parse(*this, m_yyscanner);
46,696✔
1981
    parse.set_debug_level(trace_parsing);
46,696✔
1982
    int res = parse();
46,696✔
1983
    if (parse_error) {
46,696✔
1984
        throw SyntaxError(util::format("Invalid predicate: '%1': %2", str, error_string));
724✔
1985
    }
724✔
1986
    return res;
45,972✔
1987
}
46,696✔
1988

1989
void parse(const std::string& str)
1990
{
1,004✔
1991
    ParserDriver driver;
1,004✔
1992
    driver.parse(str);
1,004✔
1993
}
1,004✔
1994

1995
std::string check_escapes(const char* str)
1996
{
498,856✔
1997
    std::string ret;
498,856✔
1998
    const char* p = strchr(str, '\\');
498,856✔
1999
    while (p) {
499,280✔
2000
        ret += std::string(str, p);
424✔
2001
        p++;
424✔
2002
        if (*p == ' ') {
424✔
2003
            ret += ' ';
144✔
2004
        }
144✔
2005
        else if (*p == 't') {
280✔
2006
            ret += '\t';
280✔
2007
        }
280✔
2008
        else if (*p == 'r') {
×
2009
            ret += '\r';
×
2010
        }
×
2011
        else if (*p == 'n') {
×
2012
            ret += '\n';
×
2013
        }
×
2014
        str = p + 1;
424✔
2015
        p = strchr(str, '\\');
424✔
2016
    }
424✔
2017
    return ret + std::string(str);
498,856✔
2018
}
498,856✔
2019

2020
} // namespace query_parser
2021

2022
Query Table::query(const std::string& query_string, const std::vector<MixedArguments::Arg>& arguments) const
2023
{
12,208✔
2024
    MixedArguments args(arguments);
12,208✔
2025
    return query(query_string, args, {});
12,208✔
2026
}
12,208✔
2027

2028
Query Table::query(const std::string& query_string, const std::vector<Mixed>& arguments) const
2029
{
24✔
2030
    MixedArguments args(arguments);
24✔
2031
    return query(query_string, args, {});
24✔
2032
}
24✔
2033

2034
Query Table::query(const std::string& query_string, const std::vector<Mixed>& arguments,
2035
                   const query_parser::KeyPathMapping& mapping) const
2036
{
1,784✔
2037
    MixedArguments args(arguments);
1,784✔
2038
    return query(query_string, args, mapping);
1,784✔
2039
}
1,784✔
2040

2041
Query Table::query(const std::string& query_string, const std::vector<MixedArguments::Arg>& arguments,
2042
                   const query_parser::KeyPathMapping& mapping) const
2043
{
352✔
2044
    MixedArguments args(arguments);
352✔
2045
    return query(query_string, args, mapping);
352✔
2046
}
352✔
2047

2048
Query Table::query(const std::string& query_string, query_parser::Arguments& args,
2049
                   const query_parser::KeyPathMapping& mapping) const
2050
{
45,676✔
2051
    ParserDriver driver(m_own_ref, args, mapping);
45,676✔
2052
    driver.parse(query_string);
45,676✔
2053
    driver.result->canonicalize();
45,676✔
2054
    return driver.result->visit(&driver).set_ordering(driver.ordering->visit(&driver));
45,676✔
2055
}
45,676✔
2056

2057
std::unique_ptr<Subexpr> LinkChain::column(const std::string& col, bool has_path)
2058
{
478,816✔
2059
    auto col_key = m_current_table->get_column_key(col);
478,816✔
2060
    if (!col_key) {
478,816✔
2061
        return nullptr;
128✔
2062
    }
128✔
2063

2064
    auto col_type{col_key.get_type()};
478,688✔
2065
    if (col_key.is_dictionary()) {
478,688✔
2066
        return create_subexpr<Dictionary>(col_key);
984✔
2067
    }
984✔
2068
    if (Table::is_link_type(col_type)) {
477,704✔
2069
        add(col_key);
×
2070
        return create_subexpr<Link>(col_key);
×
2071
    }
×
2072

2073
    if (col_key.is_set()) {
477,704✔
2074
        switch (col_type) {
3,984✔
2075
            case col_type_Int:
416✔
2076
                return create_subexpr<Set<Int>>(col_key);
416✔
2077
            case col_type_Bool:
✔
2078
                return create_subexpr<Set<Bool>>(col_key);
×
2079
            case col_type_String:
512✔
2080
                return create_subexpr<Set<String>>(col_key);
512✔
2081
            case col_type_Binary:
512✔
2082
                return create_subexpr<Set<Binary>>(col_key);
512✔
2083
            case col_type_Float:
416✔
2084
                return create_subexpr<Set<Float>>(col_key);
416✔
2085
            case col_type_Double:
416✔
2086
                return create_subexpr<Set<Double>>(col_key);
416✔
2087
            case col_type_Timestamp:
248✔
2088
                return create_subexpr<Set<Timestamp>>(col_key);
248✔
2089
            case col_type_Decimal:
416✔
2090
                return create_subexpr<Set<Decimal>>(col_key);
416✔
2091
            case col_type_UUID:
248✔
2092
                return create_subexpr<Set<UUID>>(col_key);
248✔
2093
            case col_type_ObjectId:
248✔
2094
                return create_subexpr<Set<ObjectId>>(col_key);
248✔
2095
            case col_type_Mixed:
552✔
2096
                return create_subexpr<Set<Mixed>>(col_key);
552✔
2097
            default:
✔
2098
                break;
×
2099
        }
3,984✔
2100
    }
3,984✔
2101
    else if (col_key.is_list()) {
473,720✔
2102
        switch (col_type) {
16,484✔
2103
            case col_type_Int:
3,052✔
2104
                return create_subexpr<Lst<Int>>(col_key);
3,052✔
2105
            case col_type_Bool:
880✔
2106
                return create_subexpr<Lst<Bool>>(col_key);
880✔
2107
            case col_type_String:
5,024✔
2108
                return create_subexpr<Lst<String>>(col_key);
5,024✔
2109
            case col_type_Binary:
1,660✔
2110
                return create_subexpr<Lst<Binary>>(col_key);
1,660✔
2111
            case col_type_Float:
880✔
2112
                return create_subexpr<Lst<Float>>(col_key);
880✔
2113
            case col_type_Double:
880✔
2114
                return create_subexpr<Lst<Double>>(col_key);
880✔
2115
            case col_type_Timestamp:
880✔
2116
                return create_subexpr<Lst<Timestamp>>(col_key);
880✔
2117
            case col_type_Decimal:
880✔
2118
                return create_subexpr<Lst<Decimal>>(col_key);
880✔
2119
            case col_type_UUID:
880✔
2120
                return create_subexpr<Lst<UUID>>(col_key);
880✔
2121
            case col_type_ObjectId:
880✔
2122
                return create_subexpr<Lst<ObjectId>>(col_key);
880✔
2123
            case col_type_Mixed:
588✔
2124
                return create_subexpr<Lst<Mixed>>(col_key);
588✔
2125
            default:
✔
2126
                break;
×
2127
        }
16,484✔
2128
    }
16,484✔
2129
    else {
457,236✔
2130
        // Having a path implies a collection
2131
        if (m_comparison_type && !has_path) {
457,236✔
2132
            bool has_list = false;
1,260✔
2133
            for (ColKey link_key : m_link_cols) {
1,312✔
2134
                if (link_key.is_collection() || link_key.get_type() == col_type_BackLink) {
1,312✔
2135
                    has_list = true;
1,216✔
2136
                    break;
1,216✔
2137
                }
1,216✔
2138
            }
1,312✔
2139
            if (!has_list) {
1,260✔
2140
                throw InvalidQueryError(util::format("The keypath following '%1' must contain a list",
44✔
2141
                                                     expression_cmp_type_to_str(m_comparison_type)));
44✔
2142
            }
44✔
2143
        }
1,260✔
2144

2145
        switch (col_type) {
457,192✔
2146
            case col_type_Int:
14,224✔
2147
                return create_subexpr<Int>(col_key);
14,224✔
2148
            case col_type_Bool:
224✔
2149
                return create_subexpr<Bool>(col_key);
224✔
2150
            case col_type_String:
4,800✔
2151
                return create_subexpr<String>(col_key);
4,800✔
2152
            case col_type_Binary:
2,052✔
2153
                return create_subexpr<Binary>(col_key);
2,052✔
2154
            case col_type_Float:
552✔
2155
                return create_subexpr<Float>(col_key);
552✔
2156
            case col_type_Double:
1,960✔
2157
                return create_subexpr<Double>(col_key);
1,960✔
2158
            case col_type_Timestamp:
736✔
2159
                return create_subexpr<Timestamp>(col_key);
736✔
2160
            case col_type_Decimal:
1,484✔
2161
                return create_subexpr<Decimal>(col_key);
1,484✔
2162
            case col_type_UUID:
520✔
2163
                return create_subexpr<UUID>(col_key);
520✔
2164
            case col_type_ObjectId:
428,802✔
2165
                return create_subexpr<ObjectId>(col_key);
428,802✔
2166
            case col_type_Mixed:
1,836✔
2167
                return create_subexpr<Mixed>(col_key);
1,836✔
2168
            default:
✔
2169
                break;
×
2170
        }
457,192✔
2171
    }
457,192✔
2172
    REALM_UNREACHABLE();
2173
    return nullptr;
×
2174
}
477,704✔
2175

2176
std::unique_ptr<Subexpr> LinkChain::subquery(Query subquery)
2177
{
280✔
2178
    REALM_ASSERT(m_link_cols.size() > 0);
280✔
2179
    auto col_key = m_link_cols.back();
280✔
2180
    return std::make_unique<SubQueryCount>(subquery, Columns<Link>(col_key, m_base_table, m_link_cols).link_map());
280✔
2181
}
280✔
2182

2183
template <class T>
2184
SubQuery<T> column(const Table& origin, ColKey origin_col_key, Query subquery)
2185
{
2186
    static_assert(std::is_same<T, BackLink>::value, "A subquery must involve a link list or backlink column");
2187
    return SubQuery<T>(column<T>(origin, origin_col_key), std::move(subquery));
2188
}
2189

2190
} // 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

© 2025 Coveralls, Inc