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

realm / realm-core / github_pull_request_281750

30 Oct 2023 03:37PM UTC coverage: 90.528% (-1.0%) from 91.571%
github_pull_request_281750

Pull #6073

Evergreen

jedelbo
Log free space and history sizes when opening file
Pull Request #6073: Merge next-major

95488 of 175952 branches covered (0.0%)

8973 of 12277 new or added lines in 149 files covered. (73.09%)

622 existing lines in 51 files now uncovered.

233503 of 257934 relevant lines covered (90.53%)

6533720.56 hits per line

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

85.47
/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
{
256✔
48
    switch (type) {
256✔
49
        case realm::query_parser::AggrNode::MAX:
60✔
50
            return ".@max";
60✔
51
        case realm::query_parser::AggrNode::MIN:
68✔
52
            return ".@min";
68✔
53
        case realm::query_parser::AggrNode::SUM:
64✔
54
            return ".@sum";
64✔
55
        case realm::query_parser::AggrNode::AVG:
64✔
56
            return ".@avg";
64✔
57
    }
×
58
    return "";
×
59
}
×
60

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

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

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

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

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

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

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

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

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

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

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

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

294
} // namespace
295

296
namespace realm {
297

298
namespace query_parser {
299

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

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

334
ParserNode::~ParserNode() = default;
2,385,788✔
335

336
QueryNode::~QueryNode() = default;
905,336✔
337

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

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

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

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

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

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

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

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

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

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

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

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

232,018✔
452
    auto left_type = left->get_type();
464,038✔
453
    auto right_type = right->get_type();
464,038✔
454

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

232,018✔
503
    if (left_type == type_Link && right->has_constant_evaluation()) {
464,038✔
504
        handle_typed_links(left, right, right_type);
368✔
505
    }
368✔
506
    if (right_type == type_Link && left->has_constant_evaluation()) {
464,038✔
507
        handle_typed_links(right, left, left_type);
12✔
508
    }
12✔
509

232,018✔
510
    if (left_type.is_valid() && right_type.is_valid() && !Mixed::data_types_are_comparable(left_type, right_type)) {
464,038✔
511
        throw InvalidQueryError(util::format("Unsupported comparison between type '%1' and type '%2'",
152✔
512
                                             get_data_type_name(left_type), get_data_type_name(right_type)));
152✔
513
    }
152✔
514
    if (left_type == type_TypeOfValue || right_type == type_TypeOfValue) {
463,886✔
515
        if (left_type != right_type) {
512✔
516
            throw InvalidQueryArgError(
12✔
517
                util::format("Unsupported comparison between @type and raw value: '%1' and '%2'",
12✔
518
                             get_data_type_name(left_type), get_data_type_name(right_type)));
12✔
519
        }
12✔
520
    }
463,874✔
521

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1,210✔
733
    verify_only_string_types(right_type, string_for_op(op));
2,420✔
734

1,210✔
735
    if (prop && !prop->links_exist() && right->has_single_value() &&
2,420✔
736
        (left_type == right_type || left_type == type_Mixed)) {
1,478✔
737
        auto col_key = prop->column_key();
536✔
738
        if (right_type == type_String) {
536✔
739
            StringData val = right->get_mixed().get_string();
320✔
740

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

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

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

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

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

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

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

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

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

930
std::unique_ptr<Subexpr> PropertyNode::visit(ParserDriver* drv, DataType)
931
{
474,502✔
932
    if (path->path_elems.back().is_key() && path->path_elems.back().get_key() == "@links") {
474,502✔
933
        identifier = "@links";
304✔
934
        // This is a backlink aggregate query
152✔
935
        path->path_elems.pop_back();
304✔
936
        auto link_chain = path->visit(drv, comp_type);
304✔
937
        auto sub = link_chain.get_backlink_count<Int>();
304✔
938
        return sub.clone();
304✔
939
    }
304✔
940
    m_link_chain = path->visit(drv, comp_type);
474,198✔
941
    if (!path->at_end()) {
474,198✔
942
        if (!path->current_path_elem->is_key()) {
472,478✔
943
            throw InvalidQueryError(util::format("[%1] not expected", *path->current_path_elem));
4✔
944
        }
4✔
945
        identifier = path->current_path_elem->get_key();
472,474✔
946
    }
472,474✔
947
    std::unique_ptr<Subexpr> subexpr{drv->column(m_link_chain, path)};
474,196✔
948

237,096✔
949
    Path indexes;
474,194✔
950
    while (!path->at_end()) {
477,938✔
951
        indexes.emplace_back(std::move(*(path->current_path_elem++)));
3,744✔
952
    }
3,744✔
953

237,096✔
954
    if (!indexes.empty()) {
474,194✔
955
        auto ok = false;
3,480✔
956
        const PathElement& first_index = indexes.front();
3,480✔
957
        if (indexes.size() > 1 && subexpr->get_type() != type_Mixed) {
3,480✔
NEW
958
            throw InvalidQueryError("Only Property of type 'any' can have nested collections");
×
UNCOV
959
        }
×
960
        if (auto mixed = dynamic_cast<Columns<Mixed>*>(subexpr.get())) {
3,480✔
961
            ok = true;
128✔
962
            mixed->path(indexes);
128✔
963
        }
128✔
964
        else if (auto dict = dynamic_cast<Columns<Dictionary>*>(subexpr.get())) {
3,352✔
965
            if (first_index.is_key()) {
224✔
966
                ok = true;
180✔
967
                auto trailing = first_index.get_key();
180✔
968
                if (trailing == "@values") {
180✔
969
                }
4✔
970
                else if (trailing == "@keys") {
176✔
971
                    subexpr = std::make_unique<ColumnDictionaryKeys>(*dict);
44✔
972
                }
44✔
973
                else {
132✔
974
                    dict->path(indexes);
132✔
975
                }
132✔
976
            }
180✔
977
            else if (first_index.is_all()) {
44✔
978
                ok = true;
36✔
979
                dict->path(indexes);
36✔
980
            }
36✔
981
        }
224✔
982
        else if (auto coll = dynamic_cast<Columns<Lst<Mixed>>*>(subexpr.get())) {
3,128✔
983
            ok = coll->indexes(indexes);
40✔
984
        }
40✔
985
        else if (auto coll = dynamic_cast<ColumnListBase*>(subexpr.get())) {
3,088✔
986
            if (indexes.size() == 1) {
3,080✔
987
                ok = coll->index(first_index);
3,080✔
988
            }
3,080✔
989
        }
3,080✔
990

1,740✔
991
        if (!ok) {
3,480✔
992
            if (first_index.is_key()) {
1,744✔
993
                auto trailing = first_index.get_key();
1,704✔
994
                if (!post_op && is_length_suffix(trailing)) {
1,704✔
995
                    // If 'length' is the operator, the last id in the path must be the name
844✔
996
                    // of a list property
844✔
997
                    path->path_elems.pop_back();
1,688✔
998
                    const std::string& prop = path->path_elems.back().get_key();
1,688✔
999
                    std::unique_ptr<Subexpr> subexpr{path->visit(drv, comp_type).column(prop)};
1,688✔
1000
                    if (auto list = dynamic_cast<ColumnListBase*>(subexpr.get())) {
1,688✔
1001
                        if (auto length_expr = list->get_element_length())
1,688✔
1002
                            return length_expr;
1,624✔
1003
                    }
80✔
1004
                }
1,688✔
1005
                throw InvalidQueryError(util::format("Property '%1.%2' has no property '%3'",
80✔
1006
                                                     m_link_chain.get_current_table()->get_class_name(), identifier,
80✔
1007
                                                     trailing));
80✔
1008
            }
80✔
1009
            else {
40✔
1010
                throw InvalidQueryError(util::format("Property '%1.%2' does not support index '%3'",
40✔
1011
                                                     m_link_chain.get_current_table()->get_class_name(), identifier,
40✔
1012
                                                     first_index));
40✔
1013
            }
40✔
1014
        }
472,450✔
1015
    }
3,480✔
1016
    if (post_op) {
472,450✔
1017
        return post_op->visit(drv, subexpr.get());
4,012✔
1018
    }
4,012✔
1019
    return subexpr;
468,438✔
1020
}
468,438✔
1021

1022
std::unique_ptr<Subexpr> SubqueryNode::visit(ParserDriver* drv, DataType)
1023
{
304✔
1024
    if (variable_name.size() < 2 || variable_name[0] != '$') {
304✔
1025
        throw SyntaxError(util::format("The subquery variable '%1' is invalid. The variable must start with "
4✔
1026
                                       "'$' and cannot be empty; for example '$x'.",
4✔
1027
                                       variable_name));
4✔
1028
    }
4✔
1029
    LinkChain lc = prop->path->visit(drv, prop->comp_type);
300✔
1030

150✔
1031
    ColKey col_key;
300✔
1032
    std::string identifier;
300✔
1033
    if (!prop->path->at_end()) {
300✔
1034
        identifier = prop->path->next_identifier();
8✔
1035
        col_key = lc.get_current_table()->get_column_key(identifier);
8✔
1036
    }
8✔
1037
    else {
292✔
1038
        identifier = prop->path->last_identifier();
292✔
1039
        col_key = lc.get_current_col();
292✔
1040
    }
292✔
1041

150✔
1042
    auto col_type = col_key.get_type();
300✔
1043
    if (col_key.is_list() && col_type != col_type_LinkList) {
300✔
1044
        throw InvalidQueryError(
4✔
1045
            util::format("A subquery can not operate on a list of primitive values (property '%1')", identifier));
4✔
1046
    }
4✔
1047
    if (col_type != col_type_LinkList && col_type != col_type_BackLink) {
296✔
1048
        throw InvalidQueryError(util::format("A subquery must operate on a list property, but '%1' is type '%2'",
8✔
1049
                                             identifier, realm::get_data_type_name(DataType(col_type))));
8✔
1050
    }
8✔
1051

144✔
1052
    TableRef previous_table = drv->m_base_table;
288✔
1053
    drv->m_base_table = lc.get_current_table().cast_away_const();
288✔
1054
    bool did_add = drv->m_mapping.add_mapping(drv->m_base_table, variable_name, "");
288✔
1055
    if (!did_add) {
288✔
1056
        throw InvalidQueryError(util::format("Unable to create a subquery expression with variable '%1' since an "
4✔
1057
                                             "identical variable already exists in this context",
4✔
1058
                                             variable_name));
4✔
1059
    }
4✔
1060
    Query sub = subquery->visit(drv);
284✔
1061
    drv->m_mapping.remove_mapping(drv->m_base_table, variable_name);
284✔
1062
    drv->m_base_table = previous_table;
284✔
1063

142✔
1064
    return lc.subquery(sub);
284✔
1065
}
284✔
1066

1067
std::unique_ptr<Subexpr> PostOpNode::visit(ParserDriver*, Subexpr* subexpr)
1068
{
4,012✔
1069
    if (op_type == PostOpNode::SIZE) {
4,012✔
1070
        if (auto s = dynamic_cast<Columns<Link>*>(subexpr)) {
3,428✔
1071
            return s->count().clone();
344✔
1072
        }
344✔
1073
        if (auto s = dynamic_cast<ColumnListBase*>(subexpr)) {
3,084✔
1074
            return s->size().clone();
2,896✔
1075
        }
2,896✔
1076
        if (auto s = dynamic_cast<Columns<StringData>*>(subexpr)) {
188✔
1077
            return s->size().clone();
128✔
1078
        }
128✔
1079
        if (auto s = dynamic_cast<Columns<BinaryData>*>(subexpr)) {
60✔
1080
            return s->size().clone();
48✔
1081
        }
48✔
1082
    }
584✔
1083
    else if (op_type == PostOpNode::TYPE) {
584✔
1084
        if (auto s = dynamic_cast<Columns<Mixed>*>(subexpr)) {
584✔
1085
            return s->type_of_value().clone();
452✔
1086
        }
452✔
1087
        if (auto s = dynamic_cast<ColumnsCollection<Mixed>*>(subexpr)) {
132✔
1088
            return s->type_of_value().clone();
112✔
1089
        }
112✔
1090
        if (auto s = dynamic_cast<ObjPropertyBase*>(subexpr)) {
20✔
1091
            return Value<TypeOfValue>(TypeOfValue(s->column_key())).clone();
8✔
1092
        }
8✔
1093
        if (dynamic_cast<Columns<Link>*>(subexpr)) {
12✔
1094
            return Value<TypeOfValue>(TypeOfValue(TypeOfValue::Attribute::ObjectLink)).clone();
12✔
1095
        }
12✔
1096
    }
12✔
1097

6✔
1098
    if (subexpr) {
12✔
1099
        throw InvalidQueryError(util::format("Operation '%1' is not supported on property of type '%2'", op_name,
12✔
1100
                                             get_data_type_name(DataType(subexpr->get_type()))));
12✔
1101
    }
12✔
1102
    REALM_UNREACHABLE();
×
1103
    return {};
×
1104
}
×
1105

1106
std::unique_ptr<Subexpr> LinkAggrNode::visit(ParserDriver* drv, DataType)
1107
{
784✔
1108
    auto subexpr = property->visit(drv);
784✔
1109
    auto link_prop = dynamic_cast<Columns<Link>*>(subexpr.get());
784✔
1110
    if (!link_prop) {
784✔
1111
        throw InvalidQueryError(util::format("Operation '%1' cannot apply to property '%2' because it is not a list",
40✔
1112
                                             agg_op_type_to_str(type), property->get_identifier()));
40✔
1113
    }
40✔
1114
    const LinkChain& link_chain = property->link_chain();
744✔
1115
    prop_name = drv->translate(link_chain, prop_name);
744✔
1116
    auto col_key = link_chain.get_current_table()->get_column_key(prop_name);
744✔
1117

372✔
1118
    switch (col_key.get_type()) {
744✔
1119
        case col_type_Int:
64✔
1120
            subexpr = link_prop->column<Int>(col_key).clone();
64✔
1121
            break;
64✔
1122
        case col_type_Float:
128✔
1123
            subexpr = link_prop->column<float>(col_key).clone();
128✔
1124
            break;
128✔
1125
        case col_type_Double:
344✔
1126
            subexpr = link_prop->column<double>(col_key).clone();
344✔
1127
            break;
344✔
1128
        case col_type_Decimal:
96✔
1129
            subexpr = link_prop->column<Decimal>(col_key).clone();
96✔
1130
            break;
96✔
1131
        case col_type_Timestamp:
56✔
1132
            subexpr = link_prop->column<Timestamp>(col_key).clone();
56✔
1133
            break;
56✔
1134
        default:
48✔
1135
            throw InvalidQueryError(util::format("collection aggregate not supported for type '%1'",
48✔
1136
                                                 get_data_type_name(DataType(col_key.get_type()))));
48✔
1137
    }
688✔
1138
    return aggregate(subexpr.get());
688✔
1139
}
688✔
1140

1141
std::unique_ptr<Subexpr> ListAggrNode::visit(ParserDriver* drv, DataType)
1142
{
2,728✔
1143
    auto subexpr = property->visit(drv);
2,728✔
1144
    return aggregate(subexpr.get());
2,728✔
1145
}
2,728✔
1146

1147
std::unique_ptr<Subexpr> AggrNode::aggregate(Subexpr* subexpr)
1148
{
3,416✔
1149
    std::unique_ptr<Subexpr> agg;
3,416✔
1150
    if (auto list_prop = dynamic_cast<ColumnListBase*>(subexpr)) {
3,416✔
1151
        switch (type) {
2,696✔
1152
            case MAX:
628✔
1153
                agg = list_prop->max_of();
628✔
1154
                break;
628✔
1155
            case MIN:
604✔
1156
                agg = list_prop->min_of();
604✔
1157
                break;
604✔
1158
            case SUM:
796✔
1159
                agg = list_prop->sum_of();
796✔
1160
                break;
796✔
1161
            case AVG:
668✔
1162
                agg = list_prop->avg_of();
668✔
1163
                break;
668✔
1164
        }
720✔
1165
    }
720✔
1166
    else if (auto prop = dynamic_cast<SubColumnBase*>(subexpr)) {
720✔
1167
        switch (type) {
688✔
1168
            case MAX:
200✔
1169
                agg = prop->max_of();
200✔
1170
                break;
200✔
1171
            case MIN:
192✔
1172
                agg = prop->min_of();
192✔
1173
                break;
192✔
1174
            case SUM:
132✔
1175
                agg = prop->sum_of();
132✔
1176
                break;
132✔
1177
            case AVG:
164✔
1178
                agg = prop->avg_of();
164✔
1179
                break;
164✔
1180
        }
3,416✔
1181
    }
3,416✔
1182
    if (!agg) {
3,416✔
1183
        throw InvalidQueryError(
216✔
1184
            util::format("Cannot use aggregate '%1' for this type of property", agg_op_type_to_str(type)));
216✔
1185
    }
216✔
1186

1,600✔
1187
    return agg;
3,200✔
1188
}
3,200✔
1189

1190
void ConstantNode::decode_b64(util::FunctionRef<void(StringData)> callback)
1191
{
1,432✔
1192
    const size_t encoded_size = text.size() - 5;
1,432✔
1193
    size_t buffer_size = util::base64_decoded_size(encoded_size);
1,432✔
1194
    std::string decode_buffer(buffer_size, char(0));
1,432✔
1195
    StringData window(text.c_str() + 4, encoded_size);
1,432✔
1196
    util::Optional<size_t> decoded_size = util::base64_decode(window, decode_buffer.data(), buffer_size);
1,432✔
1197
    if (!decoded_size) {
1,432✔
NEW
1198
        throw SyntaxError("Invalid base64 value");
×
NEW
1199
    }
×
1200
    REALM_ASSERT_DEBUG_EX(*decoded_size <= encoded_size, *decoded_size, encoded_size);
1,432✔
1201
    decode_buffer.resize(*decoded_size); // truncate
1,432✔
1202
    callback(StringData(decode_buffer.data(), decode_buffer.size()));
1,432✔
1203
}
1,432✔
1204

1205
std::unique_ptr<Subexpr> ConstantNode::visit(ParserDriver* drv, DataType hint)
1206
{
471,180✔
1207
    std::unique_ptr<Subexpr> ret;
471,180✔
1208
    std::string explain_value_message = text;
471,180✔
1209
    if (target_table.length()) {
471,180✔
1210
        const Group* g = drv->m_base_table->get_parent_group();
104✔
1211
        TableKey table_key;
104✔
1212
        ObjKey obj_key;
104✔
1213
        auto table = g->get_table(target_table);
104✔
1214
        if (!table) {
104✔
1215
            // Perhaps class prefix is missing
4✔
1216
            Group::TableNameBuffer buffer;
8✔
1217
            table = g->get_table(Group::class_name_to_table_name(target_table, buffer));
8✔
1218
        }
8✔
1219
        if (!table) {
104✔
1220
            throw InvalidQueryError(util::format("Unknown object type '%1'", target_table));
×
1221
        }
×
1222
        table_key = table->get_key();
104✔
1223
        target_table = "";
104✔
1224
        auto pk_val_node = visit(drv, hint); // call recursively
104✔
1225
        auto pk_val = pk_val_node->get_mixed();
104✔
1226
        obj_key = table->find_primary_key(pk_val);
104✔
1227
        return std::make_unique<Value<ObjLink>>(ObjLink(table_key, ObjKey(obj_key)));
104✔
1228
    }
104✔
1229
    switch (type) {
471,076✔
1230
        case Type::NUMBER: {
22,212✔
1231
            if (hint == type_Decimal) {
22,212✔
1232
                ret = std::make_unique<Value<Decimal128>>(Decimal128(text));
716✔
1233
            }
716✔
1234
            else {
21,496✔
1235
                ret = std::make_unique<Value<int64_t>>(strtoll(text.c_str(), nullptr, 0));
21,496✔
1236
            }
21,496✔
1237
            break;
22,212✔
1238
        }
×
1239
        case Type::FLOAT: {
2,108✔
1240
            if (hint == type_Float || text[text.size() - 1] == 'f') {
2,108✔
1241
                ret = std::make_unique<Value<float>>(strtof(text.c_str(), nullptr));
104✔
1242
            }
104✔
1243
            else if (hint == type_Decimal) {
2,004✔
1244
                ret = std::make_unique<Value<Decimal128>>(Decimal128(text));
412✔
1245
            }
412✔
1246
            else {
1,592✔
1247
                ret = std::make_unique<Value<double>>(strtod(text.c_str(), nullptr));
1,592✔
1248
            }
1,592✔
1249
            break;
2,108✔
1250
        }
×
1251
        case Type::INFINITY_VAL: {
152✔
1252
            bool negative = text[0] == '-';
152✔
1253
            switch (hint) {
152✔
1254
                case type_Float: {
48✔
1255
                    auto inf = std::numeric_limits<float>::infinity();
48✔
1256
                    ret = std::make_unique<Value<float>>(negative ? -inf : inf);
40✔
1257
                    break;
48✔
1258
                }
×
1259
                case type_Double: {
48✔
1260
                    auto inf = std::numeric_limits<double>::infinity();
48✔
1261
                    ret = std::make_unique<Value<double>>(negative ? -inf : inf);
40✔
1262
                    break;
48✔
1263
                }
×
1264
                case type_Decimal:
48✔
1265
                    ret = std::make_unique<Value<Decimal128>>(Decimal128(text));
48✔
1266
                    break;
48✔
1267
                default:
8✔
1268
                    throw InvalidQueryError(util::format("Infinity not supported for %1", get_data_type_name(hint)));
8✔
1269
                    break;
×
1270
            }
144✔
1271
            break;
144✔
1272
        }
144✔
1273
        case Type::NAN_VAL: {
104✔
1274
            switch (hint) {
64✔
1275
                case type_Float:
32✔
1276
                    ret = std::make_unique<Value<float>>(type_punning<float>(0x7fc00000));
32✔
1277
                    break;
32✔
1278
                case type_Double:
32✔
1279
                    ret = std::make_unique<Value<double>>(type_punning<double>(0x7ff8000000000000));
32✔
1280
                    break;
32✔
1281
                case type_Decimal:
✔
1282
                    ret = std::make_unique<Value<Decimal128>>(Decimal128::nan("0"));
×
1283
                    break;
×
1284
                default:
✔
1285
                    REALM_UNREACHABLE();
×
1286
                    break;
×
1287
            }
64✔
1288
            break;
64✔
1289
        }
64✔
1290
        case Type::STRING: {
6,180✔
1291
            std::string str = text.substr(1, text.size() - 2);
6,180✔
1292
            switch (hint) {
6,180✔
1293
                case type_Int:
408✔
1294
                    ret = std::make_unique<Value<int64_t>>(string_to<int64_t>(str));
408✔
1295
                    break;
408✔
1296
                case type_Float:
✔
1297
                    ret = std::make_unique<Value<float>>(string_to<float>(str));
×
1298
                    break;
×
1299
                case type_Double:
✔
1300
                    ret = std::make_unique<Value<double>>(string_to<double>(str));
×
1301
                    break;
×
1302
                case type_Decimal:
✔
1303
                    ret = std::make_unique<Value<Decimal128>>(Decimal128(str.c_str()));
×
1304
                    break;
×
1305
                default:
5,772✔
1306
                    if (hint == type_TypeOfValue) {
5,772✔
1307
                        ret = std::make_unique<Value<TypeOfValue>>(TypeOfValue(str));
496✔
1308
                    }
496✔
1309
                    else {
5,276✔
1310
                        ret = std::make_unique<ConstantStringValue>(str);
5,276✔
1311
                    }
5,276✔
1312
                    break;
5,772✔
1313
            }
6,152✔
1314
            break;
6,152✔
1315
        }
6,152✔
1316
        case Type::STRING_BASE64: {
3,434✔
1317
            decode_b64([&](StringData decoded) {
716✔
1318
                ret = std::make_unique<ConstantStringValue>(decoded);
716✔
1319
            });
716✔
1320
            break;
716✔
1321
        }
6,152✔
1322
        case Type::TIMESTAMP: {
3,382✔
1323
            auto s = text;
612✔
1324
            int64_t seconds;
612✔
1325
            int32_t nanoseconds;
612✔
1326
            if (s[0] == 'T') {
612✔
1327
                size_t colon_pos = s.find(":");
536✔
1328
                std::string s1 = s.substr(1, colon_pos - 1);
536✔
1329
                std::string s2 = s.substr(colon_pos + 1);
536✔
1330
                seconds = strtol(s1.c_str(), nullptr, 0);
536✔
1331
                nanoseconds = int32_t(strtol(s2.c_str(), nullptr, 0));
536✔
1332
            }
536✔
1333
            else {
76✔
1334
                // readable format YYYY-MM-DD-HH:MM:SS:NANOS nanos optional
38✔
1335
                struct tm tmp = tm();
76✔
1336
                char sep = s.find("@") < s.size() ? '@' : 'T';
62✔
1337
                std::string fmt = "%d-%d-%d"s + sep + "%d:%d:%d:%d"s;
76✔
1338
                int cnt = sscanf(s.c_str(), fmt.c_str(), &tmp.tm_year, &tmp.tm_mon, &tmp.tm_mday, &tmp.tm_hour,
76✔
1339
                                 &tmp.tm_min, &tmp.tm_sec, &nanoseconds);
76✔
1340
                REALM_ASSERT(cnt >= 6);
76✔
1341
                tmp.tm_year -= 1900; // epoch offset (see man mktime)
76✔
1342
                tmp.tm_mon -= 1;     // converts from 1-12 to 0-11
76✔
1343

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

30✔
1349
                seconds = platform_timegm(tmp); // UTC time
60✔
1350
                if (cnt == 6) {
60✔
1351
                    nanoseconds = 0;
32✔
1352
                }
32✔
1353
                if (nanoseconds < 0) {
60✔
1354
                    throw SyntaxError("The nanoseconds of a Timestamp cannot be negative.");
×
1355
                }
×
1356
                if (seconds < 0) { // seconds determines the sign of the nanoseconds part
60✔
1357
                    nanoseconds *= -1;
24✔
1358
                }
24✔
1359
            }
60✔
1360
            ret = std::make_unique<Value<Timestamp>>(get_timestamp_if_valid(seconds, nanoseconds));
604✔
1361
            break;
596✔
1362
        }
612✔
1363
        case Type::UUID_T:
672✔
1364
            ret = std::make_unique<Value<UUID>>(UUID(text.substr(5, text.size() - 6)));
672✔
1365
            break;
672✔
1366
        case Type::OID:
772✔
1367
            ret = std::make_unique<Value<ObjectId>>(ObjectId(text.substr(4, text.size() - 5).c_str()));
772✔
1368
            break;
772✔
1369
        case Type::LINK: {
310✔
1370
            ret =
8✔
1371
                std::make_unique<Value<ObjKey>>(ObjKey(strtol(text.substr(1, text.size() - 1).c_str(), nullptr, 0)));
8✔
1372
            break;
8✔
1373
        }
612✔
1374
        case Type::TYPED_LINK: {
330✔
1375
            size_t colon_pos = text.find(":");
48✔
1376
            auto table_key_val = uint32_t(strtol(text.substr(1, colon_pos - 1).c_str(), nullptr, 0));
48✔
1377
            auto obj_key_val = strtol(text.substr(colon_pos + 1).c_str(), nullptr, 0);
48✔
1378
            ret = std::make_unique<Value<ObjLink>>(ObjLink(TableKey(table_key_val), ObjKey(obj_key_val)));
48✔
1379
            break;
48✔
1380
        }
612✔
1381
        case Type::NULL_VAL:
2,216✔
1382
            if (hint == type_String) {
2,216✔
1383
                ret = std::make_unique<ConstantStringValue>(StringData()); // Null string
320✔
1384
            }
320✔
1385
            else if (hint == type_Binary) {
1,896✔
1386
                ret = std::make_unique<Value<Binary>>(BinaryData()); // Null string
312✔
1387
            }
312✔
1388
            else {
1,584✔
1389
                ret = std::make_unique<Value<null>>(realm::null());
1,584✔
1390
            }
1,584✔
1391
            break;
2,216✔
1392
        case Type::TRUE:
386✔
1393
            ret = std::make_unique<Value<Bool>>(true);
160✔
1394
            break;
160✔
1395
        case Type::FALSE:
462✔
1396
            ret = std::make_unique<Value<Bool>>(false);
312✔
1397
            break;
312✔
1398
        case Type::ARG: {
433,060✔
1399
            size_t arg_no = size_t(strtol(text.substr(1).c_str(), nullptr, 10));
433,060✔
1400
            if (m_comp_type && !drv->m_args.is_argument_list(arg_no)) {
433,060✔
1401
                throw InvalidQueryError(util::format(
12✔
1402
                    "ANY/ALL/NONE are only allowed on arguments which contain a list but '%1' is not a list.",
12✔
1403
                    explain_value_message));
12✔
1404
            }
12✔
1405
            if (drv->m_args.is_argument_null(arg_no)) {
433,048✔
1406
                explain_value_message = util::format("argument '%1' which is NULL", explain_value_message);
256✔
1407
                ret = std::make_unique<Value<null>>(realm::null());
256✔
1408
            }
256✔
1409
            else if (drv->m_args.is_argument_list(arg_no)) {
432,792✔
1410
                std::vector<Mixed> mixed_list = drv->m_args.list_for_argument(arg_no);
144✔
1411
                ret = copy_list_of_args(mixed_list);
144✔
1412
            }
144✔
1413
            else {
432,648✔
1414
                auto type = drv->m_args.type_for_argument(arg_no);
432,648✔
1415
                explain_value_message =
432,648✔
1416
                    util::format("argument %1 of type '%2'", explain_value_message, get_data_type_name(type));
432,648✔
1417
                ret = copy_arg(drv, type, arg_no, hint, explain_value_message);
432,648✔
1418
            }
432,648✔
1419
            break;
433,048✔
1420
        }
433,048✔
1421
        case BINARY_STR: {
217,058✔
1422
            std::string str = text.substr(1, text.size() - 2);
1,068✔
1423
            ret = std::make_unique<ConstantBinaryValue>(BinaryData(str.data(), str.size()));
1,068✔
1424
            break;
1,068✔
1425
        }
433,048✔
1426
        case BINARY_BASE64:
216,882✔
1427
            decode_b64([&](StringData decoded) {
716✔
1428
                ret = std::make_unique<ConstantBinaryValue>(BinaryData(decoded.data(), decoded.size()));
716✔
1429
            });
716✔
1430
            break;
716✔
1431
    }
470,962✔
1432
    if (!ret) {
470,962✔
1433
        throw InvalidQueryError(
×
1434
            util::format("Unsupported comparison between property of type '%1' and constant value: %2",
×
1435
                         get_data_type_name(hint), explain_value_message));
×
1436
    }
×
1437
    return ret;
470,962✔
1438
}
470,962✔
1439

1440
std::unique_ptr<ConstantMixedList> ConstantNode::copy_list_of_args(std::vector<Mixed>& mixed_args)
1441
{
144✔
1442
    std::unique_ptr<ConstantMixedList> args_in_list = std::make_unique<ConstantMixedList>(mixed_args.size());
144✔
1443
    size_t ndx = 0;
144✔
1444
    for (const auto& mixed : mixed_args) {
352✔
1445
        args_in_list->set(ndx++, mixed);
352✔
1446
    }
352✔
1447
    if (m_comp_type) {
144✔
1448
        args_in_list->set_comparison_type(*m_comp_type);
20✔
1449
    }
20✔
1450
    return args_in_list;
144✔
1451
}
144✔
1452

1453
std::unique_ptr<Subexpr> ConstantNode::copy_arg(ParserDriver* drv, DataType type, size_t arg_no, DataType hint,
1454
                                                std::string& err)
1455
{
432,616✔
1456
    switch (type) {
432,616✔
1457
        case type_Int:
828✔
1458
            return std::make_unique<Value<int64_t>>(drv->m_args.long_for_argument(arg_no));
828✔
1459
        case type_String:
388✔
1460
            return std::make_unique<ConstantStringValue>(drv->m_args.string_for_argument(arg_no));
388✔
1461
        case type_Binary:
176✔
1462
            return std::make_unique<ConstantBinaryValue>(drv->m_args.binary_for_argument(arg_no));
176✔
1463
        case type_Bool:
316✔
1464
            return std::make_unique<Value<Bool>>(drv->m_args.bool_for_argument(arg_no));
316✔
1465
        case type_Float:
436✔
1466
            return std::make_unique<Value<float>>(drv->m_args.float_for_argument(arg_no));
436✔
1467
        case type_Double: {
524✔
1468
            // In realm-js all number type arguments are returned as double. If we don't cast to the
262✔
1469
            // expected type, we would in many cases miss the option to use the optimized query node
262✔
1470
            // instead of the general Compare class.
262✔
1471
            double val = drv->m_args.double_for_argument(arg_no);
524✔
1472
            switch (hint) {
524✔
1473
                case type_Int:
6✔
1474
                case type_Bool: {
8✔
1475
                    int64_t int_val = int64_t(val);
8✔
1476
                    // Only return an integer if it precisely represents val
4✔
1477
                    if (double(int_val) == val)
8✔
1478
                        return std::make_unique<Value<int64_t>>(int_val);
×
1479
                    else
8✔
1480
                        return std::make_unique<Value<double>>(val);
8✔
1481
                }
×
1482
                case type_Float:
4✔
1483
                    return std::make_unique<Value<float>>(float(val));
4✔
1484
                default:
512✔
1485
                    return std::make_unique<Value<double>>(val);
512✔
1486
            }
×
1487
            break;
×
1488
        }
×
1489
        case type_Timestamp: {
392✔
1490
            try {
392✔
1491
                return std::make_unique<Value<Timestamp>>(drv->m_args.timestamp_for_argument(arg_no));
392✔
1492
            }
392✔
1493
            catch (const std::exception&) {
×
1494
                return std::make_unique<Value<ObjectId>>(drv->m_args.objectid_for_argument(arg_no));
×
1495
            }
×
1496
        }
×
1497
        case type_ObjectId: {
428,612✔
1498
            try {
428,612✔
1499
                return std::make_unique<Value<ObjectId>>(drv->m_args.objectid_for_argument(arg_no));
428,612✔
1500
            }
428,612✔
1501
            catch (const std::exception&) {
×
1502
                return std::make_unique<Value<Timestamp>>(drv->m_args.timestamp_for_argument(arg_no));
×
1503
            }
×
1504
            break;
×
1505
        }
×
1506
        case type_Decimal:
492✔
1507
            return std::make_unique<Value<Decimal128>>(drv->m_args.decimal128_for_argument(arg_no));
492✔
1508
        case type_UUID:
400✔
1509
            return std::make_unique<Value<UUID>>(drv->m_args.uuid_for_argument(arg_no));
400✔
1510
        case type_Link:
12✔
1511
            return std::make_unique<Value<ObjKey>>(drv->m_args.object_index_for_argument(arg_no));
12✔
1512
        case type_TypedLink:
40✔
1513
            if (hint == type_Mixed || hint == type_Link || hint == type_TypedLink) {
40!
1514
                return std::make_unique<Value<ObjLink>>(drv->m_args.objlink_for_argument(arg_no));
40✔
1515
            }
40✔
1516
            err = util::format("%1 which links to %2", err,
×
1517
                               print_pretty_objlink(drv->m_args.objlink_for_argument(arg_no),
×
1518
                                                    drv->m_base_table->get_parent_group()));
×
1519
            break;
×
1520
        default:
✔
1521
            break;
×
1522
    }
×
1523
    return nullptr;
×
1524
}
×
1525

1526
#if REALM_ENABLE_GEOSPATIAL
1527
GeospatialNode::GeospatialNode(GeospatialNode::Box, GeoPoint& p1, GeoPoint& p2)
1528
    : m_geo{Geospatial{GeoBox{p1, p2}}}
1529
{
20✔
1530
}
20✔
1531

1532
GeospatialNode::GeospatialNode(Circle, GeoPoint& p, double radius)
1533
    : m_geo{Geospatial{GeoCircle{radius, p}}}
1534
{
60✔
1535
}
60✔
1536

1537
GeospatialNode::GeospatialNode(Polygon, GeoPoint& p)
1538
    : m_points({{p}})
1539
{
×
1540
}
×
1541

1542
GeospatialNode::GeospatialNode(Loop, GeoPoint& p)
1543
    : m_points({{p}})
1544
{
68✔
1545
}
68✔
1546

1547
void GeospatialNode::add_point_to_loop(GeoPoint& p)
1548
{
272✔
1549
    m_points.back().push_back(p);
272✔
1550
}
272✔
1551

1552
void GeospatialNode::add_loop_to_polygon(GeospatialNode* node)
1553
{
8✔
1554
    m_points.push_back(node->m_points.back());
8✔
1555
}
8✔
1556

1557
std::unique_ptr<Subexpr> GeospatialNode::visit(ParserDriver*, DataType)
1558
{
124✔
1559
    std::unique_ptr<Subexpr> ret;
124✔
1560
    if (m_geo.get_type() != Geospatial::Type::Invalid) {
124✔
1561
        ret = std::make_unique<ConstantGeospatialValue>(m_geo);
72✔
1562
    }
72✔
1563
    else {
52✔
1564
        ret = std::make_unique<ConstantGeospatialValue>(GeoPolygon{m_points});
52✔
1565
    }
52✔
1566
    return ret;
124✔
1567
}
124✔
1568
#endif
1569

1570
std::unique_ptr<Subexpr> ListNode::visit(ParserDriver* drv, DataType hint)
1571
{
1,196✔
1572
    if (hint == type_TypeOfValue) {
1,196✔
1573
        try {
12✔
1574
            std::unique_ptr<Value<TypeOfValue>> ret = std::make_unique<Value<TypeOfValue>>();
12✔
1575
            constexpr bool is_list = true;
12✔
1576
            ret->init(is_list, elements.size());
12✔
1577
            ret->set_comparison_type(m_comp_type);
12✔
1578
            size_t ndx = 0;
12✔
1579
            for (auto constant : elements) {
24✔
1580
                std::unique_ptr<Subexpr> evaluated = constant->visit(drv, hint);
24✔
1581
                if (auto converted = dynamic_cast<Value<TypeOfValue>*>(evaluated.get())) {
24✔
1582
                    ret->set(ndx++, converted->get(0));
20✔
1583
                }
20✔
1584
                else {
4✔
1585
                    throw InvalidQueryError(util::format("Invalid constant inside constant list: %1",
4✔
1586
                                                         evaluated->description(drv->m_serializer_state)));
4✔
1587
                }
4✔
1588
            }
24✔
1589
            return ret;
10✔
1590
        }
×
1591
        catch (const std::runtime_error& e) {
×
1592
            throw InvalidQueryArgError(e.what());
×
1593
        }
×
1594
    }
1,184✔
1595

592✔
1596
    auto ret = std::make_unique<ConstantMixedList>(elements.size());
1,184✔
1597
    ret->set_comparison_type(m_comp_type);
1,184✔
1598
    size_t ndx = 0;
1,184✔
1599
    for (auto constant : elements) {
3,120✔
1600
        auto evaulated_constant = constant->visit(drv, hint);
3,120✔
1601
        if (auto value = dynamic_cast<const ValueBase*>(evaulated_constant.get())) {
3,120✔
1602
            REALM_ASSERT_EX(value->size() == 1, value->size());
3,120✔
1603
            ret->set(ndx++, value->get(0));
3,120✔
1604
        }
3,120✔
1605
        else {
×
1606
            throw InvalidQueryError("Invalid constant inside constant list");
×
1607
        }
×
1608
    }
3,120✔
1609
    return ret;
1,184✔
1610
}
1,184✔
1611

1612
LinkChain PathNode::visit(ParserDriver* drv, util::Optional<ExpressionComparisonType> comp_type)
1613
{
476,484✔
1614
    LinkChain link_chain(drv->m_base_table, comp_type);
476,484✔
1615
    for (current_path_elem = path_elems.begin(); current_path_elem != path_elems.end(); ++current_path_elem) {
493,370✔
1616
        if (current_path_elem->is_key()) {
491,064✔
1617
            const std::string& raw_path_elem = current_path_elem->get_key();
490,982✔
1618
            auto path_elem = drv->translate(link_chain, raw_path_elem);
490,982✔
1619
            if (path_elem.find("@links.") == 0) {
490,982✔
1620
                std::string_view table_column_pair(path_elem);
504✔
1621
                table_column_pair = table_column_pair.substr(7);
504✔
1622
                auto dot_pos = table_column_pair.find('.');
504✔
1623
                auto table_name = table_column_pair.substr(0, dot_pos);
504✔
1624
                auto column_name = table_column_pair.substr(dot_pos + 1);
504✔
1625
                drv->backlink(link_chain, table_name, column_name);
504✔
1626
                continue;
504✔
1627
            }
504✔
1628
            if (path_elem == "@values") {
490,478✔
1629
                if (!link_chain.get_current_col().is_dictionary()) {
20✔
NEW
1630
                    throw InvalidQueryError("@values only allowed on dictionaries");
×
NEW
1631
                }
×
1632
                continue;
20✔
1633
            }
20✔
1634
            if (path_elem.empty()) {
490,458✔
1635
                continue; // this element has been removed, this happens in subqueries
284✔
1636
            }
284✔
1637

245,086✔
1638
            // Check if it is a link
245,086✔
1639
            if (link_chain.link(path_elem)) {
490,174✔
1640
                continue;
15,952✔
1641
            }
15,952✔
1642
            // The next identifier being a property on the linked to object takes precedence
237,110✔
1643
            if (link_chain.get_current_table()->get_column_key(path_elem)) {
474,222✔
1644
                break;
474,050✔
1645
            }
474,050✔
1646
        }
254✔
1647
        if (!link_chain.index(*current_path_elem))
254✔
1648
            break;
128✔
1649
    }
254✔
1650
    return link_chain;
476,484✔
1651
}
476,484✔
1652

1653
DescriptorNode::~DescriptorNode() {}
1,404✔
1654

1655
DescriptorOrderingNode::~DescriptorOrderingNode() {}
42,200✔
1656

1657
std::unique_ptr<DescriptorOrdering> DescriptorOrderingNode::visit(ParserDriver* drv)
1658
{
39,840✔
1659
    auto target = drv->m_base_table;
39,840✔
1660
    std::unique_ptr<DescriptorOrdering> ordering;
39,840✔
1661
    for (auto cur_ordering : orderings) {
20,490✔
1662
        if (!ordering)
1,140✔
1663
            ordering = std::make_unique<DescriptorOrdering>();
708✔
1664
        if (cur_ordering->get_type() == DescriptorNode::LIMIT) {
1,140✔
1665
            ordering->append_limit(LimitDescriptor(cur_ordering->limit));
340✔
1666
        }
340✔
1667
        else {
800✔
1668
            bool is_distinct = cur_ordering->get_type() == DescriptorNode::DISTINCT;
800✔
1669
            std::vector<std::vector<ExtendedColumnKey>> property_columns;
800✔
1670
            for (Path& path : cur_ordering->columns) {
836✔
1671
                std::vector<ExtendedColumnKey> columns;
836✔
1672
                LinkChain link_chain(target);
836✔
1673
                ColKey col_key;
836✔
1674
                for (size_t ndx_in_path = 0; ndx_in_path < path.size(); ++ndx_in_path) {
1,780✔
1675
                    std::string prop_name = drv->translate(link_chain, path[ndx_in_path].get_key());
952✔
1676
                    // If last column was a dictionary, We will treat the next entry as a key to
476✔
1677
                    // the dictionary
476✔
1678
                    if (col_key && col_key.is_dictionary()) {
952✔
1679
                        columns.back().set_index(prop_name);
32✔
1680
                    }
32✔
1681
                    else {
920✔
1682
                        col_key = link_chain.get_current_table()->get_column_key(prop_name);
920✔
1683
                        if (!col_key) {
920✔
1684
                            throw InvalidQueryError(util::format(
8✔
1685
                                "No property '%1' found on object type '%2' specified in '%3' clause", prop_name,
8✔
1686
                                link_chain.get_current_table()->get_class_name(), is_distinct ? "distinct" : "sort"));
6✔
1687
                        }
8✔
1688
                        columns.emplace_back(col_key);
912✔
1689
                        if (ndx_in_path < path.size() - 1) {
912✔
1690
                            link_chain.link(col_key);
116✔
1691
                        }
116✔
1692
                    }
912✔
1693
                }
952✔
1694
                property_columns.push_back(columns);
832✔
1695
            }
828✔
1696

400✔
1697
            if (is_distinct) {
796✔
1698
                ordering->append_distinct(DistinctDescriptor(property_columns));
252✔
1699
            }
252✔
1700
            else {
540✔
1701
                ordering->append_sort(SortDescriptor(property_columns, cur_ordering->ascending),
540✔
1702
                                      SortDescriptor::MergeMode::prepend);
540✔
1703
            }
540✔
1704
        }
792✔
1705
    }
1,140✔
1706

19,920✔
1707
    return ordering;
39,836✔
1708
}
39,840✔
1709

1710
// If one of the expresions is constant, it should be right
1711
static void verify_conditions(Subexpr* left, Subexpr* right, util::serializer::SerialisationState& state)
1712
{
470,844✔
1713
    if (dynamic_cast<ColumnListBase*>(left) && dynamic_cast<ColumnListBase*>(right)) {
470,844✔
1714
        throw InvalidQueryError(
32✔
1715
            util::format("Ordered comparison between two primitive lists is not implemented yet ('%1' and '%2')",
32✔
1716
                         left->description(state), right->description(state)));
32✔
1717
    }
32✔
1718
    if (dynamic_cast<Value<TypeOfValue>*>(left) && dynamic_cast<Value<TypeOfValue>*>(right)) {
470,812✔
1719
        throw InvalidQueryError(util::format("Comparison between two constants is not supported ('%1' and '%2')",
8✔
1720
                                             left->description(state), right->description(state)));
8✔
1721
    }
8✔
1722
    if (auto link_column = dynamic_cast<Columns<Link>*>(left)) {
470,804✔
1723
        if (link_column->has_multiple_values() && right->has_single_value() && right->get_mixed().is_null()) {
408✔
1724
            throw InvalidQueryError(
20✔
1725
                util::format("Cannot compare linklist ('%1') with NULL", left->description(state)));
20✔
1726
        }
20✔
1727
    }
408✔
1728
}
470,804✔
1729

1730
ParserDriver::ParserDriver(TableRef t, Arguments& args, const query_parser::KeyPathMapping& mapping)
1731
    : m_base_table(t)
1732
    , m_args(args)
1733
    , m_mapping(mapping)
1734
{
42,660✔
1735
    yylex_init(&m_yyscanner);
42,660✔
1736
}
42,660✔
1737

1738
ParserDriver::~ParserDriver()
1739
{
42,658✔
1740
    yylex_destroy(m_yyscanner);
42,658✔
1741
}
42,658✔
1742

1743
PathElement ParserDriver::get_arg_for_index(const std::string& i)
1744
{
4✔
1745
    REALM_ASSERT(i[0] == '$');
4✔
1746
    size_t arg_no = size_t(strtol(i.substr(1).c_str(), nullptr, 10));
4✔
1747
    if (m_args.is_argument_null(arg_no) || m_args.is_argument_list(arg_no)) {
4✔
1748
        throw InvalidQueryError("Invalid index parameter");
×
1749
    }
×
1750
    auto type = m_args.type_for_argument(arg_no);
4✔
1751
    switch (type) {
4✔
1752
        case type_Int:
✔
NEW
1753
            return size_t(m_args.long_for_argument(arg_no));
×
1754
        case type_String:
4✔
1755
            return m_args.string_for_argument(arg_no);
4✔
1756
        default:
✔
1757
            throw InvalidQueryError("Invalid index type");
×
1758
    }
4✔
1759
}
4✔
1760

1761
double ParserDriver::get_arg_for_coordinate(const std::string& str)
1762
{
80✔
1763
    REALM_ASSERT(str[0] == '$');
80✔
1764
    size_t arg_no = size_t(strtol(str.substr(1).c_str(), nullptr, 10));
80✔
1765
    if (m_args.is_argument_null(arg_no)) {
80✔
1766
        throw InvalidQueryError(util::format("NULL cannot be used in coordinate at argument '%1'", str));
4✔
1767
    }
4✔
1768
    if (m_args.is_argument_list(arg_no)) {
76✔
1769
        throw InvalidQueryError(util::format("A list cannot be used in a coordinate at argument '%1'", str));
×
1770
    }
×
1771

38✔
1772
    auto type = m_args.type_for_argument(arg_no);
76✔
1773
    switch (type) {
76✔
1774
        case type_Int:
✔
1775
            return double(m_args.long_for_argument(arg_no));
×
1776
        case type_Double:
68✔
1777
            return m_args.double_for_argument(arg_no);
68✔
1778
        case type_Float:
✔
1779
            return double(m_args.float_for_argument(arg_no));
×
1780
        default:
8✔
1781
            throw InvalidQueryError(util::format("Invalid parameter '%1' used in coordinate at argument '%2'",
8✔
1782
                                                 get_data_type_name(type), str));
8✔
1783
    }
76✔
1784
}
76✔
1785

1786
auto ParserDriver::cmp(const std::vector<ExpressionNode*>& values) -> std::pair<SubexprPtr, SubexprPtr>
1787
{
471,620✔
1788
    SubexprPtr left;
471,620✔
1789
    SubexprPtr right;
471,620✔
1790

235,808✔
1791
    auto left_is_constant = values[0]->is_constant();
471,620✔
1792
    auto right_is_constant = values[1]->is_constant();
471,620✔
1793

235,808✔
1794
    if (left_is_constant && right_is_constant) {
471,620✔
1795
        throw InvalidQueryError("Cannot compare two constants");
×
1796
    }
×
1797

235,808✔
1798
    if (right_is_constant) {
471,620✔
1799
        // Take left first - it cannot be a constant
233,844✔
1800
        left = values[0]->visit(this);
467,688✔
1801
        right = values[1]->visit(this, left->get_type());
467,688✔
1802
        verify_conditions(left.get(), right.get(), m_serializer_state);
467,688✔
1803
    }
467,688✔
1804
    else {
3,932✔
1805
        right = values[1]->visit(this);
3,932✔
1806
        if (left_is_constant) {
3,932✔
1807
            left = values[0]->visit(this, right->get_type());
1,408✔
1808
        }
1,408✔
1809
        else {
2,524✔
1810
            left = values[0]->visit(this);
2,524✔
1811
        }
2,524✔
1812
        verify_conditions(right.get(), left.get(), m_serializer_state);
3,932✔
1813
    }
3,932✔
1814
    return {std::move(left), std::move(right)};
471,620✔
1815
}
471,620✔
1816

1817
auto ParserDriver::column(LinkChain& link_chain, PathNode* path) -> SubexprPtr
1818
{
474,186✔
1819
    if (path->at_end()) {
474,186✔
1820
        // This is a link property. However Columns<Link> does not handle @keys and indexes
854✔
1821
        auto extended_col_key = link_chain.m_link_cols.back();
1,708✔
1822
        if (!extended_col_key.has_index()) {
1,708✔
1823
            return link_chain.create_subexpr<Link>(ColKey(extended_col_key));
1,704✔
1824
        }
1,704✔
1825
        link_chain.pop_back();
4✔
1826
        --path->current_path_elem;
4✔
1827
        --path->current_path_elem;
4✔
1828
    }
4✔
1829
    auto identifier = m_mapping.translate(link_chain, path->next_identifier());
473,334✔
1830
    if (auto col = link_chain.column(identifier)) {
472,482✔
1831
        return col;
472,314✔
1832
    }
472,314✔
1833
    throw InvalidQueryError(
168✔
1834
        util::format("'%1' has no property '%2'", link_chain.get_current_table()->get_class_name(), identifier));
168✔
1835
}
168✔
1836

1837
void ParserDriver::backlink(LinkChain& link_chain, std::string_view raw_table_name, std::string_view raw_column_name)
1838
{
504✔
1839
    std::string table_name = m_mapping.translate_table_name(raw_table_name);
504✔
1840
    auto origin_table = m_base_table->get_parent_group()->get_table(table_name);
504✔
1841
    ColKey origin_column;
504✔
1842
    std::string column_name{raw_column_name};
504✔
1843
    if (origin_table) {
504✔
1844
        column_name = m_mapping.translate(origin_table, column_name);
496✔
1845
        origin_column = origin_table->get_column_key(column_name);
496✔
1846
    }
496✔
1847
    if (!origin_column) {
504✔
1848
        auto origin_table_name = Group::table_name_to_class_name(table_name);
12✔
1849
        auto current_table_name = link_chain.get_current_table()->get_class_name();
12✔
1850
        throw InvalidQueryError(util::format("No property '%1' found in type '%2' which links to type '%3'",
12✔
1851
                                             column_name, origin_table_name, current_table_name));
12✔
1852
    }
12✔
1853
    link_chain.backlink(*origin_table, origin_column);
492✔
1854
}
492✔
1855

1856
std::string ParserDriver::translate(const LinkChain& link_chain, const std::string& identifier)
1857
{
492,674✔
1858
    return m_mapping.translate(link_chain, identifier);
492,674✔
1859
}
492,674✔
1860

1861
int ParserDriver::parse(const std::string& str)
1862
{
42,660✔
1863
    // std::cout << str << std::endl;
21,330✔
1864
    parse_buffer.append(str);
42,660✔
1865
    parse_buffer.append("\0\0", 2); // Flex requires 2 terminating zeroes
42,660✔
1866
    scan_begin(m_yyscanner, trace_scanning);
42,660✔
1867
    yy::parser parse(*this, m_yyscanner);
42,660✔
1868
    parse.set_debug_level(trace_parsing);
42,660✔
1869
    int res = parse();
42,660✔
1870
    if (parse_error) {
42,660✔
1871
        throw SyntaxError(util::format("Invalid predicate: '%1': %2", str, error_string));
724✔
1872
    }
724✔
1873
    return res;
41,936✔
1874
}
41,936✔
1875

1876
void parse(const std::string& str)
1877
{
1,004✔
1878
    ParserDriver driver;
1,004✔
1879
    driver.parse(str);
1,004✔
1880
}
1,004✔
1881

1882
std::string check_escapes(const char* str)
1883
{
493,678✔
1884
    std::string ret;
493,678✔
1885
    const char* p = strchr(str, '\\');
493,678✔
1886
    while (p) {
494,102✔
1887
        ret += std::string(str, p);
424✔
1888
        p++;
424✔
1889
        if (*p == ' ') {
424✔
1890
            ret += ' ';
144✔
1891
        }
144✔
1892
        else if (*p == 't') {
280✔
1893
            ret += '\t';
280✔
1894
        }
280✔
1895
        else if (*p == 'r') {
×
1896
            ret += '\r';
×
1897
        }
×
1898
        else if (*p == 'n') {
×
1899
            ret += '\n';
×
1900
        }
×
1901
        str = p + 1;
424✔
1902
        p = strchr(str, '\\');
424✔
1903
    }
424✔
1904
    return ret + std::string(str);
493,678✔
1905
}
493,678✔
1906

1907
} // namespace query_parser
1908

1909
Query Table::query(const std::string& query_string, const std::vector<MixedArguments::Arg>& arguments) const
1910
{
11,368✔
1911
    MixedArguments args(arguments);
11,368✔
1912
    return query(query_string, args, {});
11,368✔
1913
}
11,368✔
1914

1915
Query Table::query(const std::string& query_string, const std::vector<Mixed>& arguments) const
1916
{
4✔
1917
    MixedArguments args(arguments);
4✔
1918
    return query(query_string, args, {});
4✔
1919
}
4✔
1920

1921
Query Table::query(const std::string& query_string, const std::vector<Mixed>& arguments,
1922
                   const query_parser::KeyPathMapping& mapping) const
1923
{
1,688✔
1924
    MixedArguments args(arguments);
1,688✔
1925
    return query(query_string, args, mapping);
1,688✔
1926
}
1,688✔
1927

1928
Query Table::query(const std::string& query_string, const std::vector<MixedArguments::Arg>& arguments,
1929
                   const query_parser::KeyPathMapping& mapping) const
1930
{
320✔
1931
    MixedArguments args(arguments);
320✔
1932
    return query(query_string, args, mapping);
320✔
1933
}
320✔
1934

1935
Query Table::query(const std::string& query_string, query_parser::Arguments& args,
1936
                   const query_parser::KeyPathMapping& mapping) const
1937
{
41,636✔
1938
    ParserDriver driver(m_own_ref, args, mapping);
41,636✔
1939
    driver.parse(query_string);
41,636✔
1940
    driver.result->canonicalize();
41,636✔
1941
    return driver.result->visit(&driver).set_ordering(driver.ordering->visit(&driver));
41,636✔
1942
}
41,636✔
1943

1944
std::unique_ptr<Subexpr> LinkChain::column(const std::string& col)
1945
{
474,172✔
1946
    auto col_key = m_current_table->get_column_key(col);
474,172✔
1947
    if (!col_key) {
474,172✔
1948
        return nullptr;
124✔
1949
    }
124✔
1950
    size_t list_count = 0;
474,048✔
1951
    for (ColKey link_key : m_link_cols) {
244,104✔
1952
        if (link_key.get_type() == col_type_LinkList || link_key.get_type() == col_type_BackLink) {
14,160✔
1953
            list_count++;
2,112✔
1954
        }
2,112✔
1955
    }
14,160✔
1956

237,024✔
1957
    auto col_type{col_key.get_type()};
474,048✔
1958
    if (col_key.is_dictionary()) {
474,048✔
1959
        return create_subexpr<Dictionary>(col_key);
952✔
1960
    }
952✔
1961
    if (Table::is_link_type(col_type)) {
473,096✔
UNCOV
1962
        add(col_key);
×
UNCOV
1963
        return create_subexpr<Link>(col_key);
×
UNCOV
1964
    }
×
1965

236,548✔
1966
    if (col_key.is_set()) {
473,096✔
1967
        switch (col_type) {
3,984✔
1968
            case col_type_Int:
416✔
1969
                return create_subexpr<Set<Int>>(col_key);
416✔
1970
            case col_type_Bool:
✔
1971
                return create_subexpr<Set<Bool>>(col_key);
×
1972
            case col_type_String:
512✔
1973
                return create_subexpr<Set<String>>(col_key);
512✔
1974
            case col_type_Binary:
512✔
1975
                return create_subexpr<Set<Binary>>(col_key);
512✔
1976
            case col_type_Float:
416✔
1977
                return create_subexpr<Set<Float>>(col_key);
416✔
1978
            case col_type_Double:
416✔
1979
                return create_subexpr<Set<Double>>(col_key);
416✔
1980
            case col_type_Timestamp:
248✔
1981
                return create_subexpr<Set<Timestamp>>(col_key);
248✔
1982
            case col_type_Decimal:
416✔
1983
                return create_subexpr<Set<Decimal>>(col_key);
416✔
1984
            case col_type_UUID:
248✔
1985
                return create_subexpr<Set<UUID>>(col_key);
248✔
1986
            case col_type_ObjectId:
248✔
1987
                return create_subexpr<Set<ObjectId>>(col_key);
248✔
1988
            case col_type_Mixed:
552✔
1989
                return create_subexpr<Set<Mixed>>(col_key);
552✔
1990
            default:
✔
1991
                break;
×
1992
        }
469,112✔
1993
    }
469,112✔
1994
    else if (col_key.is_list()) {
469,112✔
1995
        switch (col_type) {
14,712✔
1996
            case col_type_Int:
3,040✔
1997
                return create_subexpr<Lst<Int>>(col_key);
3,040✔
1998
            case col_type_Bool:
880✔
1999
                return create_subexpr<Lst<Bool>>(col_key);
880✔
2000
            case col_type_String:
3,276✔
2001
                return create_subexpr<Lst<String>>(col_key);
3,276✔
2002
            case col_type_Binary:
1,660✔
2003
                return create_subexpr<Lst<Binary>>(col_key);
1,660✔
2004
            case col_type_Float:
880✔
2005
                return create_subexpr<Lst<Float>>(col_key);
880✔
2006
            case col_type_Double:
880✔
2007
                return create_subexpr<Lst<Double>>(col_key);
880✔
2008
            case col_type_Timestamp:
880✔
2009
                return create_subexpr<Lst<Timestamp>>(col_key);
880✔
2010
            case col_type_Decimal:
880✔
2011
                return create_subexpr<Lst<Decimal>>(col_key);
880✔
2012
            case col_type_UUID:
880✔
2013
                return create_subexpr<Lst<UUID>>(col_key);
880✔
2014
            case col_type_ObjectId:
880✔
2015
                return create_subexpr<Lst<ObjectId>>(col_key);
880✔
2016
            case col_type_Mixed:
576✔
2017
                return create_subexpr<Lst<Mixed>>(col_key);
576✔
2018
            default:
✔
2019
                break;
×
2020
        }
454,400✔
2021
    }
454,400✔
2022
    else {
454,400✔
2023
        if (m_comparison_type && list_count == 0) {
454,400✔
2024
            throw InvalidQueryError(util::format("The keypath following '%1' must contain a list",
44✔
2025
                                                 expression_cmp_type_to_str(m_comparison_type)));
44✔
2026
        }
44✔
2027

227,178✔
2028
        switch (col_type) {
454,356✔
2029
            case col_type_Int:
12,932✔
2030
                return create_subexpr<Int>(col_key);
12,932✔
2031
            case col_type_Bool:
224✔
2032
                return create_subexpr<Bool>(col_key);
224✔
2033
            case col_type_String:
4,440✔
2034
                return create_subexpr<String>(col_key);
4,440✔
2035
            case col_type_Binary:
2,052✔
2036
                return create_subexpr<Binary>(col_key);
2,052✔
2037
            case col_type_Float:
552✔
2038
                return create_subexpr<Float>(col_key);
552✔
2039
            case col_type_Double:
1,944✔
2040
                return create_subexpr<Double>(col_key);
1,944✔
2041
            case col_type_Timestamp:
728✔
2042
                return create_subexpr<Timestamp>(col_key);
728✔
2043
            case col_type_Decimal:
1,068✔
2044
                return create_subexpr<Decimal>(col_key);
1,068✔
2045
            case col_type_UUID:
520✔
2046
                return create_subexpr<UUID>(col_key);
520✔
2047
            case col_type_ObjectId:
428,804✔
2048
                return create_subexpr<ObjectId>(col_key);
428,804✔
2049
            case col_type_Mixed:
1,092✔
2050
                return create_subexpr<Mixed>(col_key);
1,092✔
2051
            default:
✔
2052
                break;
×
2053
        }
×
2054
    }
×
2055
    REALM_UNREACHABLE();
×
2056
    return nullptr;
×
2057
}
×
2058

2059
std::unique_ptr<Subexpr> LinkChain::subquery(Query subquery)
2060
{
280✔
2061
    REALM_ASSERT(m_link_cols.size() > 0);
280✔
2062
    auto col_key = m_link_cols.back();
280✔
2063
    return std::make_unique<SubQueryCount>(subquery, Columns<Link>(col_key, m_base_table, m_link_cols).link_map());
280✔
2064
}
280✔
2065

2066
template <class T>
2067
SubQuery<T> column(const Table& origin, ColKey origin_col_key, Query subquery)
2068
{
2069
    static_assert(std::is_same<T, BackLink>::value, "A subquery must involve a link list or backlink column");
2070
    return SubQuery<T>(column<T>(origin, origin_col_key), std::move(subquery));
2071
}
2072

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