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

realm / realm-core / github_pull_request_312964

19 Feb 2025 07:31PM UTC coverage: 90.814% (-0.3%) from 91.119%
github_pull_request_312964

Pull #8071

Evergreen

web-flow
Bump serialize-javascript and mocha

Bumps [serialize-javascript](https://github.com/yahoo/serialize-javascript) to 6.0.2 and updates ancestor dependency [mocha](https://github.com/mochajs/mocha). These dependencies need to be updated together.


Updates `serialize-javascript` from 6.0.0 to 6.0.2
- [Release notes](https://github.com/yahoo/serialize-javascript/releases)
- [Commits](https://github.com/yahoo/serialize-javascript/compare/v6.0.0...v6.0.2)

Updates `mocha` from 10.2.0 to 10.8.2
- [Release notes](https://github.com/mochajs/mocha/releases)
- [Changelog](https://github.com/mochajs/mocha/blob/main/CHANGELOG.md)
- [Commits](https://github.com/mochajs/mocha/compare/v10.2.0...v10.8.2)

---
updated-dependencies:
- dependency-name: serialize-javascript
  dependency-type: indirect
- dependency-name: mocha
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Pull Request #8071: Bump serialize-javascript and mocha

96552 of 179126 branches covered (53.9%)

212672 of 234185 relevant lines covered (90.81%)

3115802.0 hits per line

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

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

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

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

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

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

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

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

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

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

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

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

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

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

283
} // namespace
284

285
namespace realm {
286

287
namespace query_parser {
288

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

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

323
ParserNode::~ParserNode() = default;
1,205,250✔
324

325
QueryNode::~QueryNode() = default;
454,912✔
326

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

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

345
    return q;
292✔
346
}
292✔
347

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

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

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

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

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

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

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

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

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

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

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

492
    if (left_type == type_Link && right->has_constant_evaluation()) {
233,764✔
493
        handle_typed_links(left, right, right_type);
194✔
494
    }
194✔
495
    if (right_type == type_Link && left->has_constant_evaluation()) {
233,764✔
496
        handle_typed_links(right, left, left_type);
6✔
497
    }
6✔
498

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

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

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

532
    if (left_type == type_Link && left_type == right_type && right->has_constant_evaluation()) {
233,566✔
533
        if (auto link_column = dynamic_cast<const Columns<Link>*>(left.get())) {
190✔
534
            if (link_column->link_map().get_nb_hops() == 1 &&
190✔
535
                link_column->get_comparison_type().value_or(ExpressionComparisonType::Any) ==
190✔
536
                    ExpressionComparisonType::Any) {
162✔
537
                REALM_ASSERT(dynamic_cast<const Value<ObjKey>*>(right.get()));
154✔
538
                auto link_values = static_cast<const Value<ObjKey>*>(right.get());
154✔
539
                // We can use a LinksToNode based query
540
                std::vector<ObjKey> values;
154✔
541
                values.reserve(link_values->size());
154✔
542
                for (auto val : *link_values) {
162✔
543
                    values.emplace_back(val.is_null() ? ObjKey() : val.get<ObjKey>());
162✔
544
                }
162✔
545
                if (op == CompareType::EQUAL) {
154✔
546
                    return drv->m_base_table->where().links_to(link_column->link_map().get_first_column_key(),
100✔
547
                                                               values);
100✔
548
                }
100✔
549
                else if (op == CompareType::NOT_EQUAL) {
54✔
550
                    return drv->m_base_table->where().not_links_to(link_column->link_map().get_first_column_key(),
52✔
551
                                                                   values);
52✔
552
                }
52✔
553
            }
154✔
554
        }
190✔
555
    }
190✔
556
    else if (right->has_single_value() && (left_type == right_type || left_type == type_Mixed)) {
233,376✔
557
        Mixed val = right->get_mixed();
229,636✔
558
        const ObjPropertyBase* prop = dynamic_cast<const ObjPropertyBase*>(left.get());
229,636✔
559
        if (prop && !prop->links_exist() && !prop->has_path()) {
229,636✔
560
            auto col_key = prop->column_key();
221,846✔
561
            if (val.is_null()) {
221,846✔
562
                switch (op) {
106✔
563
                    case CompareType::EQUAL:
78✔
564
                    case CompareType::IN:
78✔
565
                        return drv->m_base_table->where().equal(col_key, realm::null());
78✔
566
                    case CompareType::NOT_EQUAL:
28✔
567
                        return drv->m_base_table->where().not_equal(col_key, realm::null());
28✔
568
                    default:
✔
569
                        break;
×
570
                }
106✔
571
            }
106✔
572
            switch (left->get_type()) {
221,740✔
573
                case type_Int:
4,918✔
574
                    return drv->simple_query(op, col_key, val.get_int());
4,918✔
575
                case type_Bool:
66✔
576
                    return drv->simple_query(op, col_key, val.get_bool());
66✔
577
                case type_String:
1,196✔
578
                    return drv->simple_query(op, col_key, val.get_string(), case_sensitive);
1,196✔
579
                case type_Binary:
708✔
580
                    return drv->simple_query(op, col_key, val.get_binary(), case_sensitive);
708✔
581
                case type_Timestamp:
70✔
582
                    return drv->simple_query(op, col_key, val.get<Timestamp>());
70✔
583
                case type_Float:
26✔
584
                    return drv->simple_query(op, col_key, val.get_float());
26✔
585
                case type_Double:
50✔
586
                    return drv->simple_query(op, col_key, val.get_double());
50✔
587
                case type_Decimal:
376✔
588
                    return drv->simple_query(op, col_key, val.get<Decimal128>());
376✔
589
                case type_ObjectId:
214,192✔
590
                    return drv->simple_query(op, col_key, val.get<ObjectId>());
214,192✔
591
                case type_UUID:
82✔
592
                    return drv->simple_query(op, col_key, val.get<UUID>());
82✔
593
                case type_Mixed:
60✔
594
                    return drv->simple_query(op, col_key, val, case_sensitive);
60✔
595
                default:
✔
596
                    break;
×
597
            }
221,740✔
598
        }
221,740✔
599
    }
229,636✔
600
    if (case_sensitive) {
11,568✔
601
        switch (op) {
11,156✔
602
            case CompareType::EQUAL:
9,138✔
603
            case CompareType::IN:
9,420✔
604
                return Query(std::unique_ptr<Expression>(new Compare<Equal>(std::move(left), std::move(right))));
9,420✔
605
            case CompareType::NOT_EQUAL:
1,736✔
606
                return Query(std::unique_ptr<Expression>(new Compare<NotEqual>(std::move(left), std::move(right))));
1,736✔
607
            default:
✔
608
                break;
×
609
        }
11,156✔
610
    }
11,156✔
611
    else {
412✔
612
        verify_only_string_types(right_type, util::format("%1%2", string_for_op(op), "[c]"));
412✔
613
        switch (op) {
412✔
614
            case CompareType::EQUAL:
52✔
615
            case CompareType::IN:
56✔
616
                return Query(std::unique_ptr<Expression>(new Compare<EqualIns>(std::move(left), std::move(right))));
56✔
617
            case CompareType::NOT_EQUAL:
20✔
618
                return Query(
20✔
619
                    std::unique_ptr<Expression>(new Compare<NotEqualIns>(std::move(left), std::move(right))));
20✔
620
            default:
✔
621
                break;
×
622
        }
412✔
623
    }
412✔
624
    return {};
×
625
}
11,568✔
626

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

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

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

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

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

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

669
    return q;
24✔
670
}
98✔
671

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

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

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

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

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

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

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

754
    verify_only_string_types(right_type, string_for_op(op));
1,428✔
755

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

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

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

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

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

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

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

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

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

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

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

971
    Path indexes;
239,396✔
972
    while (!path->at_end()) {
242,150✔
973
        indexes.emplace_back(std::move(*(path->current_path_elem++)));
2,754✔
974
    }
2,754✔
975

976
    if (!indexes.empty()) {
239,396✔
977
        auto ok = false;
2,076✔
978
        const PathElement& first_index = indexes.front();
2,076✔
979
        if (indexes.size() > 1 && subexpr->get_type() != type_Mixed) {
2,076✔
980
            throw InvalidQueryError("Only Property of type 'any' can have nested collections");
×
981
        }
×
982
        if (auto mixed = dynamic_cast<Columns<Mixed>*>(subexpr.get())) {
2,076✔
983
            ok = true;
372✔
984
            mixed->path(indexes);
372✔
985
        }
372✔
986
        else if (auto dict = dynamic_cast<Columns<Dictionary>*>(subexpr.get())) {
1,704✔
987
            if (first_index.is_key()) {
128✔
988
                ok = true;
98✔
989
                auto trailing = first_index.get_key();
98✔
990
                if (trailing == "@values") {
98✔
991
                }
2✔
992
                else if (trailing == "@keys") {
96✔
993
                    subexpr = std::make_unique<ColumnDictionaryKeys>(*dict);
26✔
994
                }
26✔
995
                else {
70✔
996
                    dict->path(indexes);
70✔
997
                }
70✔
998
            }
98✔
999
            else if (first_index.is_all()) {
30✔
1000
                ok = true;
26✔
1001
                dict->path(indexes);
26✔
1002
            }
26✔
1003
        }
128✔
1004
        else if (auto coll = dynamic_cast<Columns<Lst<Mixed>>*>(subexpr.get())) {
1,576✔
1005
            ok = coll->indexes(indexes);
24✔
1006
        }
24✔
1007
        else if (auto coll = dynamic_cast<ColumnListBase*>(subexpr.get())) {
1,552✔
1008
            if (indexes.size() == 1) {
1,548✔
1009
                ok = coll->index(first_index);
1,548✔
1010
            }
1,548✔
1011
        }
1,548✔
1012

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

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

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

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

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

1087
    return lc.subquery(sub);
142✔
1088
}
144✔
1089

1090
std::unique_ptr<Subexpr> PostOpNode::visit(ParserDriver*, Subexpr* subexpr)
1091
{
2,176✔
1092
    if (op_type == PostOpNode::SIZE) {
2,176✔
1093
        if (auto s = dynamic_cast<Columns<Link>*>(subexpr)) {
1,792✔
1094
            return s->count().clone();
172✔
1095
        }
172✔
1096
        if (auto s = dynamic_cast<ColumnListBase*>(subexpr)) {
1,620✔
1097
            return s->size().clone();
1,464✔
1098
        }
1,464✔
1099
        if (auto s = dynamic_cast<Columns<StringData>*>(subexpr)) {
156✔
1100
            return s->size().clone();
64✔
1101
        }
64✔
1102
        if (auto s = dynamic_cast<Columns<BinaryData>*>(subexpr)) {
92✔
1103
            return s->size().clone();
24✔
1104
        }
24✔
1105
        if (auto s = dynamic_cast<Columns<Mixed>*>(subexpr)) {
68✔
1106
            return s->size().clone();
62✔
1107
        }
62✔
1108
    }
68✔
1109
    else if (op_type == PostOpNode::TYPE) {
384✔
1110
        if (auto s = dynamic_cast<Columns<Mixed>*>(subexpr)) {
384✔
1111
            return s->type_of_value().clone();
318✔
1112
        }
318✔
1113
        if (auto s = dynamic_cast<ColumnsCollection<Mixed>*>(subexpr)) {
66✔
1114
            return s->type_of_value().clone();
56✔
1115
        }
56✔
1116
        if (auto s = dynamic_cast<ObjPropertyBase*>(subexpr)) {
10✔
1117
            return Value<TypeOfValue>(TypeOfValue(s->column_key())).clone();
4✔
1118
        }
4✔
1119
        if (dynamic_cast<Columns<Link>*>(subexpr)) {
6✔
1120
            return Value<TypeOfValue>(TypeOfValue(TypeOfValue::Attribute::ObjectLink)).clone();
6✔
1121
        }
6✔
1122
    }
6✔
1123

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

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

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

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

1176
std::unique_ptr<Subexpr> AggrNode::aggregate(Subexpr* subexpr)
1177
{
1,748✔
1178
    std::unique_ptr<Subexpr> agg;
1,748✔
1179
    if (auto list_prop = dynamic_cast<ColumnListBase*>(subexpr)) {
1,748✔
1180
        switch (type) {
1,356✔
1181
            case MAX:
316✔
1182
                agg = list_prop->max_of();
316✔
1183
                break;
316✔
1184
            case MIN:
304✔
1185
                agg = list_prop->min_of();
304✔
1186
                break;
304✔
1187
            case SUM:
400✔
1188
                agg = list_prop->sum_of();
400✔
1189
                break;
400✔
1190
            case AVG:
336✔
1191
                agg = list_prop->avg_of();
336✔
1192
                break;
336✔
1193
        }
1,356✔
1194
    }
1,356✔
1195
    else if (auto prop = dynamic_cast<SubColumnBase*>(subexpr)) {
392✔
1196
        switch (type) {
376✔
1197
            case MAX:
108✔
1198
                agg = prop->max_of();
108✔
1199
                break;
108✔
1200
            case MIN:
104✔
1201
                agg = prop->min_of();
104✔
1202
                break;
104✔
1203
            case SUM:
74✔
1204
                agg = prop->sum_of();
74✔
1205
                break;
74✔
1206
            case AVG:
90✔
1207
                agg = prop->avg_of();
90✔
1208
                break;
90✔
1209
        }
376✔
1210
    }
376✔
1211
    if (!agg) {
1,748✔
1212
        throw InvalidQueryError(
116✔
1213
            util::format("Cannot use aggregate '%1' for this type of property", agg_op_type_to_str(type)));
116✔
1214
    }
116✔
1215

1216
    return agg;
1,632✔
1217
}
1,748✔
1218

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

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

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

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

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

1331
    auto convert_if_needed = [&](Mixed& value) -> void {
238,062✔
1332
        switch (value.get_type()) {
236,862✔
1333
            case type_Int:
12,896✔
1334
                if (hint == type_Decimal) {
12,896✔
1335
                    value = Decimal128(value.get_int());
648✔
1336
                }
648✔
1337
                break;
12,896✔
1338
            case type_Double: {
1,472✔
1339
                auto double_val = value.get_double();
1,472✔
1340
                if (std::isinf(double_val) && (!Mixed::is_numeric(hint) || hint == type_Int)) {
1,472✔
1341
                    throw InvalidQueryError(util::format("Infinity not supported for %1", get_data_type_name(hint)));
4✔
1342
                }
4✔
1343

1344
                switch (hint) {
1,468✔
1345
                    case type_Float:
92✔
1346
                        value = float(double_val);
92✔
1347
                        break;
92✔
1348
                    case type_Decimal:
240✔
1349
                        // If not argument, try decode again to get full precision
1350
                        value = (type == Type::ARG) ? Decimal128(double_val) : Decimal128(text);
240✔
1351
                        break;
240✔
1352
                    case type_Int: {
40✔
1353
                        int64_t int_val = int64_t(double_val);
40✔
1354
                        // Only return an integer if it precisely represents val
1355
                        if (double(int_val) == double_val) {
40✔
1356
                            value = int_val;
12✔
1357
                        }
12✔
1358
                        break;
40✔
1359
                    }
×
1360
                    default:
1,096✔
1361
                        break;
1,096✔
1362
                }
1,468✔
1363
                break;
1,468✔
1364
            }
1,468✔
1365
            case type_Float: {
1,468✔
1366
                if (hint == type_Int) {
246✔
1367
                    float float_val = value.get_float();
26✔
1368
                    if (std::isinf(float_val)) {
26✔
1369
                        throw InvalidQueryError(
4✔
1370
                            util::format("Infinity not supported for %1", get_data_type_name(hint)));
4✔
1371
                    }
4✔
1372
                    if (std::isnan(float_val)) {
22✔
1373
                        throw InvalidQueryError(util::format("NaN not supported for %1", get_data_type_name(hint)));
4✔
1374
                    }
4✔
1375
                    int64_t int_val = int64_t(float_val);
18✔
1376
                    if (float(int_val) == float_val) {
18✔
1377
                        value = int_val;
8✔
1378
                    }
8✔
1379
                }
18✔
1380
                break;
238✔
1381
            }
246✔
1382
            case type_String: {
4,740✔
1383
                StringData str = value.get_string();
4,740✔
1384
                switch (hint) {
4,740✔
1385
                    case type_Int:
210✔
1386
                        if (auto val = string_to<int64_t>(str)) {
210✔
1387
                            value = *val;
202✔
1388
                        }
202✔
1389
                        break;
210✔
1390
                    case type_Float:
4✔
1391
                        if (auto val = string_to<float>(str)) {
4✔
1392
                            value = *val;
×
1393
                        }
×
1394
                        break;
4✔
1395
                    case type_Double:
4✔
1396
                        if (auto val = string_to<double>(str)) {
4✔
1397
                            value = *val;
×
1398
                        }
×
1399
                        break;
4✔
1400
                    case type_Decimal:
2✔
1401
                        if (auto val = string_to<Decimal128>(str)) {
2✔
1402
                            value = *val;
×
1403
                        }
×
1404
                        break;
2✔
1405
                    default:
4,520✔
1406
                        break;
4,520✔
1407
                }
4,740✔
1408
                break;
4,740✔
1409
            }
4,740✔
1410
            default:
217,508✔
1411
                break;
217,508✔
1412
        }
236,862✔
1413
    };
236,862✔
1414

1415
    if (type == Type::ARG) {
238,062✔
1416
        size_t arg_no = size_t(strtol(text.substr(1).c_str(), nullptr, 10));
216,606✔
1417
        if (m_comp_type && !drv->m_args.is_argument_list(arg_no)) {
216,606✔
1418
            throw InvalidQueryError(util::format(
6✔
1419
                "ANY/ALL/NONE are only allowed on arguments which contain a list but '%1' is not a list.",
6✔
1420
                explain_value_message));
6✔
1421
        }
6✔
1422
        if (drv->m_args.is_argument_list(arg_no)) {
216,600✔
1423
            std::vector<Mixed> mixed_list = drv->m_args.list_for_argument(arg_no);
80✔
1424
            for (auto& mixed : mixed_list) {
204✔
1425
                if (!mixed.is_null()) {
204✔
1426
                    convert_if_needed(mixed);
198✔
1427
                }
198✔
1428
            }
204✔
1429
            return copy_list_of_args(mixed_list);
80✔
1430
        }
80✔
1431
        if (drv->m_args.is_argument_null(arg_no)) {
216,520✔
1432
            explain_value_message = util::format("argument '%1' which is NULL", explain_value_message);
128✔
1433
        }
128✔
1434
        else {
216,392✔
1435
            value = drv->m_args.mixed_for_argument(arg_no);
216,392✔
1436
            if (value.is_null()) {
216,392✔
1437
                explain_value_message = util::format("argument %1 of type null", explain_value_message);
2✔
1438
            }
2✔
1439
            else if (value.is_type(type_TypedLink)) {
216,390✔
1440
                explain_value_message =
18✔
1441
                    util::format("%1 which links to %2", explain_value_message,
18✔
1442
                                 print_pretty_objlink(value.get<ObjLink>(), drv->m_base_table->get_parent_group()));
18✔
1443
            }
18✔
1444
            else {
216,372✔
1445
                explain_value_message = util::format("argument %1 with value '%2'", explain_value_message, value);
216,372✔
1446
            }
216,372✔
1447
        }
216,392✔
1448
    }
216,520✔
1449
    else {
21,456✔
1450
        value = get_value();
21,456✔
1451
    }
21,456✔
1452

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

1469
    if (value.is_null()) {
237,976✔
1470
        if (hint == type_String) {
1,280✔
1471
            return std::make_unique<ConstantStringValue>(StringData()); // Null string
174✔
1472
        }
174✔
1473
        else if (hint == type_Binary) {
1,106✔
1474
            return std::make_unique<Value<Binary>>(BinaryData()); // Null string
160✔
1475
        }
160✔
1476
        else {
946✔
1477
            return std::make_unique<Value<null>>(realm::null());
946✔
1478
        }
946✔
1479
    }
1,280✔
1480

1481
    convert_if_needed(value);
236,696✔
1482

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

1490
    switch (value.get_type()) {
236,608✔
1491
        case type_Int: {
12,358✔
1492
            ret = std::make_unique<Value<int64_t>>(value.get_int());
12,358✔
1493
            break;
12,358✔
1494
        }
×
1495
        case type_Float: {
308✔
1496
            ret = std::make_unique<Value<float>>(value.get_float());
308✔
1497
            break;
308✔
1498
        }
×
1499
        case type_Decimal:
1,132✔
1500
            ret = std::make_unique<Value<Decimal128>>(value.get_decimal());
1,132✔
1501
            break;
1,132✔
1502
        case type_Double: {
1,102✔
1503
            ret = std::make_unique<Value<double>>(value.get_double());
1,102✔
1504
            break;
1,102✔
1505
        }
×
1506
        case type_String: {
4,448✔
1507
            StringData str = value.get_string();
4,448✔
1508
            if (hint == type_TypeOfValue) {
4,448✔
1509
                TypeOfValue type_of_value(std::string_view(str.data(), str.size()));
340✔
1510
                ret = std::make_unique<Value<TypeOfValue>>(type_of_value);
340✔
1511
            }
340✔
1512
            else {
4,108✔
1513
                ret = std::make_unique<ConstantStringValue>(str);
4,108✔
1514
            }
4,108✔
1515
            break;
4,448✔
1516
        }
×
1517
        case type_Timestamp:
526✔
1518
            ret = std::make_unique<Value<Timestamp>>(value.get_timestamp());
526✔
1519
            break;
526✔
1520
        case type_UUID:
532✔
1521
            ret = std::make_unique<Value<UUID>>(value.get_uuid());
532✔
1522
            break;
532✔
1523
        case type_ObjectId:
214,696✔
1524
            ret = std::make_unique<Value<ObjectId>>(value.get_object_id());
214,696✔
1525
            break;
214,696✔
1526
        case type_Link:
10✔
1527
            ret = std::make_unique<Value<ObjKey>>(value.get<ObjKey>());
10✔
1528
            break;
10✔
1529
        case type_TypedLink:
98✔
1530
            ret = std::make_unique<Value<ObjLink>>(value.get<ObjLink>());
98✔
1531
            break;
98✔
1532
        case type_Bool:
388✔
1533
            ret = std::make_unique<Value<Bool>>(value.get_bool());
388✔
1534
            break;
388✔
1535
        case type_Binary:
968✔
1536
            ret = std::make_unique<ConstantBinaryValue>(value.get_binary());
968✔
1537
            break;
968✔
1538
        case type_Mixed:
✔
1539
            break;
×
1540
    }
236,608✔
1541
    if (!ret) {
236,556✔
1542
        throw InvalidQueryError(
×
1543
            util::format("Unsupported comparison between property of type '%1' and constant value: %2",
×
1544
                         get_data_type_name(hint), explain_value_message));
×
1545
    }
×
1546
    return ret;
236,556✔
1547
}
236,556✔
1548

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

1562
Mixed Arguments::mixed_for_argument(size_t arg_no)
1563
{
1,994✔
1564
    switch (type_for_argument(arg_no)) {
1,994✔
1565
        case type_Int:
232✔
1566
            return int64_t(long_for_argument(arg_no));
232✔
1567
        case type_String:
190✔
1568
            return string_for_argument(arg_no);
190✔
1569
        case type_Binary:
88✔
1570
            return binary_for_argument(arg_no);
88✔
1571
        case type_Bool:
158✔
1572
            return bool_for_argument(arg_no);
158✔
1573
        case type_Float:
218✔
1574
            return float_for_argument(arg_no);
218✔
1575
        case type_Double:
220✔
1576
            return double_for_argument(arg_no);
220✔
1577
        case type_Timestamp:
196✔
1578
            try {
196✔
1579
                return timestamp_for_argument(arg_no);
196✔
1580
            }
196✔
1581
            catch (const std::exception&) {
196✔
1582
            }
×
1583
            return objectid_for_argument(arg_no);
×
1584
        case type_ObjectId:
262✔
1585
            try {
262✔
1586
                return objectid_for_argument(arg_no);
262✔
1587
            }
262✔
1588
            catch (const std::exception&) {
262✔
1589
            }
×
1590
            return timestamp_for_argument(arg_no);
×
1591
        case type_Decimal:
210✔
1592
            return decimal128_for_argument(arg_no);
210✔
1593
        case type_UUID:
194✔
1594
            return uuid_for_argument(arg_no);
194✔
1595
        case type_Link:
6✔
1596
            return object_index_for_argument(arg_no);
6✔
1597
        case type_TypedLink:
20✔
1598
            return objlink_for_argument(arg_no);
20✔
1599
        default:
✔
1600
            break;
×
1601
    }
1,994✔
1602
    return {};
×
1603
}
1,994✔
1604

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

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

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

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

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

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

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

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

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

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

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

1734
            // Check if it is a link
1735
            if (link_chain.link(path_elem)) {
247,482✔
1736
                continue;
8,120✔
1737
            }
8,120✔
1738
            // The next identifier being a property on the linked to object takes precedence
1739
            if (link_chain.get_current_table()->get_column_key(path_elem)) {
239,362✔
1740
                break;
239,266✔
1741
            }
239,266✔
1742
        }
239,362✔
1743
        if (!link_chain.index(*current_path_elem))
138✔
1744
            break;
66✔
1745
    }
138✔
1746
    return link_chain;
240,534✔
1747
}
240,534✔
1748

1749
DescriptorNode::~DescriptorNode() {}
702✔
1750

1751
DescriptorOrderingNode::~DescriptorOrderingNode() {}
23,168✔
1752

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

1793
            if (is_distinct) {
396✔
1794
                ordering->append_distinct(DistinctDescriptor(property_columns));
126✔
1795
            }
126✔
1796
            else {
270✔
1797
                ordering->append_sort(SortDescriptor(property_columns, cur_ordering->ascending),
270✔
1798
                                      SortDescriptor::MergeMode::prepend);
270✔
1799
            }
270✔
1800
        }
396✔
1801
    }
570✔
1802

1803
    return ordering;
21,960✔
1804
}
21,964✔
1805

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2020
} // namespace query_parser
2021

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

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

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

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

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

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

2064
    auto col_type{col_key.get_type()};
239,276✔
2065
    if (col_key.is_dictionary()) {
239,276✔
2066
        return create_subexpr<Dictionary>(col_key);
492✔
2067
    }
492✔
2068
    if (Table::is_link_type(col_type)) {
238,784✔
2069
        add(col_key);
×
2070
        return create_subexpr<Link>(col_key);
×
2071
    }
×
2072

2073
    if (col_key.is_set()) {
238,784✔
2074
        switch (col_type) {
1,992✔
2075
            case col_type_Int:
208✔
2076
                return create_subexpr<Set<Int>>(col_key);
208✔
2077
            case col_type_Bool:
✔
2078
                return create_subexpr<Set<Bool>>(col_key);
×
2079
            case col_type_String:
256✔
2080
                return create_subexpr<Set<String>>(col_key);
256✔
2081
            case col_type_Binary:
256✔
2082
                return create_subexpr<Set<Binary>>(col_key);
256✔
2083
            case col_type_Float:
208✔
2084
                return create_subexpr<Set<Float>>(col_key);
208✔
2085
            case col_type_Double:
208✔
2086
                return create_subexpr<Set<Double>>(col_key);
208✔
2087
            case col_type_Timestamp:
124✔
2088
                return create_subexpr<Set<Timestamp>>(col_key);
124✔
2089
            case col_type_Decimal:
208✔
2090
                return create_subexpr<Set<Decimal>>(col_key);
208✔
2091
            case col_type_UUID:
124✔
2092
                return create_subexpr<Set<UUID>>(col_key);
124✔
2093
            case col_type_ObjectId:
124✔
2094
                return create_subexpr<Set<ObjectId>>(col_key);
124✔
2095
            case col_type_Mixed:
276✔
2096
                return create_subexpr<Set<Mixed>>(col_key);
276✔
2097
            default:
✔
2098
                break;
×
2099
        }
1,992✔
2100
    }
1,992✔
2101
    else if (col_key.is_list()) {
236,792✔
2102
        switch (col_type) {
8,242✔
2103
            case col_type_Int:
1,526✔
2104
                return create_subexpr<Lst<Int>>(col_key);
1,526✔
2105
            case col_type_Bool:
440✔
2106
                return create_subexpr<Lst<Bool>>(col_key);
440✔
2107
            case col_type_String:
2,512✔
2108
                return create_subexpr<Lst<String>>(col_key);
2,512✔
2109
            case col_type_Binary:
830✔
2110
                return create_subexpr<Lst<Binary>>(col_key);
830✔
2111
            case col_type_Float:
440✔
2112
                return create_subexpr<Lst<Float>>(col_key);
440✔
2113
            case col_type_Double:
440✔
2114
                return create_subexpr<Lst<Double>>(col_key);
440✔
2115
            case col_type_Timestamp:
440✔
2116
                return create_subexpr<Lst<Timestamp>>(col_key);
440✔
2117
            case col_type_Decimal:
440✔
2118
                return create_subexpr<Lst<Decimal>>(col_key);
440✔
2119
            case col_type_UUID:
440✔
2120
                return create_subexpr<Lst<UUID>>(col_key);
440✔
2121
            case col_type_ObjectId:
440✔
2122
                return create_subexpr<Lst<ObjectId>>(col_key);
440✔
2123
            case col_type_Mixed:
294✔
2124
                return create_subexpr<Lst<Mixed>>(col_key);
294✔
2125
            default:
✔
2126
                break;
×
2127
        }
8,242✔
2128
    }
8,242✔
2129
    else {
228,550✔
2130
        // Having a path implies a collection
2131
        if (m_comparison_type && !has_path) {
228,550✔
2132
            bool has_list = false;
630✔
2133
            for (ColKey link_key : m_link_cols) {
656✔
2134
                if (link_key.is_collection() || link_key.get_type() == col_type_BackLink) {
656✔
2135
                    has_list = true;
608✔
2136
                    break;
608✔
2137
                }
608✔
2138
            }
656✔
2139
            if (!has_list) {
630✔
2140
                throw InvalidQueryError(util::format("The keypath following '%1' must contain a list",
22✔
2141
                                                     expression_cmp_type_to_str(m_comparison_type)));
22✔
2142
            }
22✔
2143
        }
630✔
2144

2145
        switch (col_type) {
228,528✔
2146
            case col_type_Int:
7,082✔
2147
                return create_subexpr<Int>(col_key);
7,082✔
2148
            case col_type_Bool:
112✔
2149
                return create_subexpr<Bool>(col_key);
112✔
2150
            case col_type_String:
2,304✔
2151
                return create_subexpr<String>(col_key);
2,304✔
2152
            case col_type_Binary:
1,026✔
2153
                return create_subexpr<Binary>(col_key);
1,026✔
2154
            case col_type_Float:
276✔
2155
                return create_subexpr<Float>(col_key);
276✔
2156
            case col_type_Double:
980✔
2157
                return create_subexpr<Double>(col_key);
980✔
2158
            case col_type_Timestamp:
428✔
2159
                return create_subexpr<Timestamp>(col_key);
428✔
2160
            case col_type_Decimal:
742✔
2161
                return create_subexpr<Decimal>(col_key);
742✔
2162
            case col_type_UUID:
260✔
2163
                return create_subexpr<UUID>(col_key);
260✔
2164
            case col_type_ObjectId:
214,402✔
2165
                return create_subexpr<ObjectId>(col_key);
214,402✔
2166
            case col_type_Mixed:
918✔
2167
                return create_subexpr<Mixed>(col_key);
918✔
2168
            default:
✔
2169
                break;
×
2170
        }
228,528✔
2171
    }
228,528✔
2172
    REALM_UNREACHABLE();
2173
    return nullptr;
×
2174
}
238,784✔
2175

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

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

2190
} // namespace realm
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc