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

realm / realm-core / github_pull_request_281750

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

Pull #6073

Evergreen

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

95488 of 175952 branches covered (0.0%)

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

622 existing lines in 51 files now uncovered.

233503 of 257934 relevant lines covered (90.53%)

6533720.56 hits per line

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

92.05
/src/realm/parser/driver.hpp
1
#ifndef DRIVER_HH
2
#define DRIVER_HH
3
#include <string>
4
#include <map>
5

6
#include "realm/query_expression.hpp"
7
#include "realm/parser/keypath_mapping.hpp"
8
#include "realm/parser/query_parser.hpp"
9

10
#define YY_DECL yy::parser::symbol_type yylex(void* yyscanner)
11
#include "realm/parser/generated/query_bison.hpp"
12
YY_DECL;
13

14
#undef FALSE
15
#undef TRUE
16
#undef IN
17

18
namespace realm {
19

20
namespace query_parser {
21

22
class ParserNode {
23
public:
24
    virtual ~ParserNode();
25
};
26

27
/******************************** Query Nodes ********************************/
28

29
class QueryNode : public ParserNode {
30
public:
31
    ~QueryNode() override;
32
    virtual Query visit(ParserDriver*) = 0;
33
    virtual void canonicalize() {}
472,058✔
34
};
35

36
class TrueOrFalseNode : public QueryNode {
37
public:
38
    TrueOrFalseNode(bool type)
39
        : true_or_false(type)
40
    {
696✔
41
    }
696✔
42
    Query visit(ParserDriver*);
43

44
protected:
45
    bool true_or_false;
46
};
47

48
class LogicalNode : public QueryNode {
49
public:
50
    std::vector<QueryNode*> children;
51
    LogicalNode(QueryNode* left, QueryNode* right)
52
    {
430,840✔
53
        children.emplace_back(left);
430,840✔
54
        children.emplace_back(right);
430,840✔
55
    }
430,840✔
56
    void canonicalize() override
57
    {
1,072✔
58
        std::vector<LogicalNode*> todo;
1,072✔
59
        do_canonicalize(todo);
1,072✔
60
        while (todo.size()) {
1,240✔
61
            LogicalNode* cur = todo.back();
168✔
62
            todo.pop_back();
168✔
63
            cur->do_canonicalize(todo);
168✔
64
        }
168✔
65
    }
1,072✔
66

67
    void do_canonicalize(std::vector<LogicalNode*>& todo)
68
    {
1,240✔
69
        auto& my_type = typeid(*this);
1,240✔
70
        size_t index = 0;
1,240✔
71
        while (index < children.size()) {
862,768✔
72
            QueryNode* child = *(children.begin() + index);
861,528✔
73
            auto& child_type = typeid(*child);
861,528✔
74
            if (child_type == my_type) {
861,528✔
75
                auto logical_node = static_cast<LogicalNode*>(child);
429,524✔
76
                REALM_ASSERT_EX(logical_node->children.size() == 2, logical_node->children.size());
429,524✔
77
                children.push_back(logical_node->children[0]);
429,524✔
78
                children.push_back(logical_node->children[1]);
429,524✔
79
                children.erase(children.begin() + index);
429,524✔
80
                continue; // do not ++index because of the delete
429,524✔
81
            }
429,524✔
82
            else if (auto ln = dynamic_cast<LogicalNode*>(child)) {
432,004✔
83
                todo.push_back(ln);
168✔
84
            }
168✔
85
            else {
431,836✔
86
                child->canonicalize();
431,836✔
87
            }
431,836✔
88
            ++index;
646,766✔
89
        }
432,004✔
90
    }
1,240✔
91

92
private:
93
    virtual std::string get_operator() const = 0;
94
};
95

96
class AndNode : public LogicalNode {
97
public:
98
    using LogicalNode::LogicalNode;
99
    Query visit(ParserDriver*) override;
100

101
private:
102
    std::string get_operator() const override
103
    {
×
104
        return " && ";
×
105
    }
×
106
};
107

108
class OrNode : public LogicalNode {
109
public:
110
    using LogicalNode::LogicalNode;
111
    Query visit(ParserDriver*) override;
112

113
private:
114
    std::string get_operator() const override
115
    {
×
116
        return " || ";
×
117
    }
×
118
};
119

120
class NotNode : public QueryNode {
121
public:
122
    QueryNode* query = nullptr;
123

124
    NotNode(QueryNode* q)
125
        : query(q)
126
    {
952✔
127
    }
952✔
128
    Query visit(ParserDriver*) override;
129
};
130

131
/****************************** Expression Nodes *****************************/
132

133
class ExpressionNode : public ParserNode {
134
public:
135
    virtual bool is_constant()
136
    {
475,558✔
137
        return false;
475,558✔
138
    }
475,558✔
139
    virtual bool is_list()
140
    {
3,436✔
141
        return false;
3,436✔
142
    }
3,436✔
143
    virtual std::unique_ptr<Subexpr> visit(ParserDriver*, DataType = type_Int) = 0;
144
};
145

146
/******************************** Value Nodes ********************************/
147

148
class ValueNode : public ExpressionNode {
149
};
150

151
class ConstantNode : public ValueNode {
152
public:
153
    enum Type {
154
        NUMBER,
155
        INFINITY_VAL,
156
        NAN_VAL,
157
        FLOAT,
158
        STRING,
159
        STRING_BASE64,
160
        BINARY_STR,
161
        BINARY_BASE64,
162
        TIMESTAMP,
163
        UUID_T,
164
        OID,
165
        LINK,
166
        TYPED_LINK,
167
        NULL_VAL,
168
        TRUE,
169
        FALSE,
170
        ARG,
171
    };
172

173
    Type type;
174
    std::string text;
175

176

177
    ConstantNode(Type t, const std::string& str)
178
        : type(t)
179
        , text(str)
180
    {
472,726✔
181
    }
472,726✔
182
    ConstantNode(ExpressionComparisonType comp_type, const std::string& str)
183
        : type(Type::ARG)
184
        , text(str)
185
        , m_comp_type(comp_type)
186
    {
32✔
187
    }
32✔
188
    bool is_constant() final
189
    {
468,832✔
190
        return true;
468,832✔
191
    }
468,832✔
192
    void add_table(std::string table_name)
193
    {
104✔
194
        target_table = table_name.substr(1, table_name.size() - 2);
104✔
195
    }
104✔
196

197
    std::unique_ptr<ConstantMixedList> copy_list_of_args(std::vector<Mixed>&);
198
    std::unique_ptr<Subexpr> copy_arg(ParserDriver*, DataType, size_t, DataType, std::string&);
199
    std::unique_ptr<Subexpr> visit(ParserDriver*, DataType) override;
200
    util::Optional<ExpressionComparisonType> m_comp_type;
201
    std::string target_table;
202

203
private:
204
    void decode_b64(util::FunctionRef<void(StringData)>);
205
};
206

207
class GeospatialNode : public ValueNode {
208
public:
209
    struct Box {};
210
    struct Polygon {};
211
    struct Loop {};
212
    struct Circle {};
213
#if REALM_ENABLE_GEOSPATIAL
214
    GeospatialNode(Box, GeoPoint& p1, GeoPoint& p2);
215
    GeospatialNode(Circle, GeoPoint& p, double radius);
216
    GeospatialNode(Polygon, GeoPoint& p);
217
    GeospatialNode(Loop, GeoPoint& p);
218
    void add_point_to_loop(GeoPoint& p);
219
    void add_loop_to_polygon(GeospatialNode*);
220
    bool is_constant() final
221
    {
×
222
        return true;
×
223
    }
×
224
    std::unique_ptr<Subexpr> visit(ParserDriver*, DataType) override;
225
    std::vector<std::vector<GeoPoint>> m_points;
226
    Geospatial m_geo;
227
#else
228
    template <typename... Ts>
229
    GeospatialNode(Ts&&...)
230
    {
231
        throw realm::LogicError(ErrorCodes::NotSupported, "Support for Geospatial queries is not enabled");
232
    }
233
    template <typename Point>
234
    void add_point_to_loop(Point&&)
235
    {
236
    }
237
    template <typename Loop>
238
    void add_loop_to_polygon(Loop&&)
239
    {
240
    }
241
    std::unique_ptr<Subexpr> visit(ParserDriver*, DataType) override
242
    {
243
        return {};
244
    }
245
#endif
246
};
247

248
class ListNode : public ValueNode {
249
public:
250
    std::vector<ConstantNode*> elements;
251

252
    ListNode() = default;
144✔
253
    ListNode(ConstantNode* elem)
254
    {
1,260✔
255
        elements.emplace_back(elem);
1,260✔
256
    }
1,260✔
257
    bool is_constant() final
258
    {
1,236✔
259
        return true;
1,236✔
260
    }
1,236✔
261
    bool is_list() final
262
    {
28✔
263
        return true;
28✔
264
    }
28✔
265
    void add_element(ConstantNode* elem)
266
    {
2,300✔
267
        elements.emplace_back(elem);
2,300✔
268
    }
2,300✔
269
    void set_comp_type(ExpressionComparisonType comp_type)
270
    {
420✔
271
        m_comp_type = comp_type;
420✔
272
    }
420✔
273
    std::unique_ptr<Subexpr> visit(ParserDriver*, DataType);
274

275
private:
276
    util::Optional<ExpressionComparisonType> m_comp_type;
277
};
278

279
class PathNode : public ParserNode {
280
public:
281
    Path path_elems;
282
    Path::iterator current_path_elem;
283

284
    PathNode(const PathElement& first)
285
    {
477,250✔
286
        add_element(first);
477,250✔
287
    }
477,250✔
288
    bool at_end() const
289
    {
1,426,416✔
290
        return current_path_elem == path_elems.end();
1,426,416✔
291
    }
1,426,416✔
292
    const std::string& next_identifier()
293
    {
472,486✔
294
        return (current_path_elem++)->get_key();
472,486✔
295
    }
472,486✔
296
    const std::string& last_identifier()
297
    {
292✔
298
        return path_elems.back().get_key();
292✔
299
    }
292✔
300

301
    LinkChain visit(ParserDriver*, util::Optional<ExpressionComparisonType> = util::none);
302
    void add_element(const PathElement& elem)
303
    {
495,626✔
304
        if (backlink) {
495,626✔
305
            if (!elem.is_key()) {
948✔
NEW
306
                throw yy::parser::syntax_error("An ID must follow @links");
×
NEW
307
            }
×
308
            backlink_str += "." + elem.get_key();
948✔
309
            if (backlink == 2) {
948✔
310
                path_elems.push_back(backlink_str);
472✔
311
                backlink = 0;
472✔
312
            }
472✔
313
            else {
476✔
314
                backlink++;
476✔
315
            }
476✔
316
        }
948✔
317
        else {
494,678✔
318
            if (elem.is_key() && elem.get_key() == "@links") {
494,678✔
319
                backlink = 1;
788✔
320
                backlink_str = "@links";
788✔
321
            }
788✔
322
            else {
493,890✔
323
                path_elems.push_back(elem);
493,890✔
324
            }
493,890✔
325
        }
494,678✔
326
    }
495,626✔
327
    void finish()
328
    {
476,086✔
329
        if (backlink) {
476,086✔
330
            path_elems.push_back(backlink_str);
312✔
331
        }
312✔
332
    }
476,086✔
333

334
private:
335
    std::string backlink_str;
336
    int backlink = 0;
337
};
338

339
class PropertyNode : public ValueNode {
340
public:
341
    PathNode* path;
342
    util::Optional<ExpressionComparisonType> comp_type = util::none;
343
    PostOpNode* post_op = nullptr;
344

345
    PropertyNode(PathNode* path, util::Optional<ExpressionComparisonType> ct = util::none)
346
        : path(path)
347
        , comp_type(ct)
348
    {
476,088✔
349
        path->finish();
476,088✔
350
    }
476,088✔
351
    const std::string& get_identifier() const
352
    {
40✔
353
        return identifier;
40✔
354
    }
40✔
355
    const LinkChain& link_chain() const
356
    {
744✔
357
        return m_link_chain;
744✔
358
    }
744✔
359
    void add_postop(PostOpNode* po)
360
    {
472,206✔
361
        post_op = po;
472,206✔
362
    }
472,206✔
363
    std::unique_ptr<Subexpr> visit(ParserDriver*, DataType = type_Int) override;
364

365
private:
366
    LinkChain m_link_chain;
367
    std::string identifier;
368
};
369

370
class AggrNode : public ValueNode {
371
public:
372
    enum Type { MAX, MIN, SUM, AVG };
373

374
protected:
375
    PropertyNode* property;
376
    Type type;
377

378
    AggrNode(PropertyNode* node, int t)
379
        : property(node)
380
        , type(Type(t))
381
    {
3,520✔
382
    }
3,520✔
383
    std::unique_ptr<Subexpr> aggregate(Subexpr*);
384
};
385

386
class ListAggrNode : public AggrNode {
387
public:
388
    ListAggrNode(PropertyNode* node, int t)
389
        : AggrNode(node, t)
390
    {
2,728✔
391
    }
2,728✔
392

393
protected:
394
    std::unique_ptr<Subexpr> visit(ParserDriver*, DataType) override;
395
};
396

397
class LinkAggrNode : public AggrNode {
398
public:
399
    LinkAggrNode(PropertyNode* node, int t, std::string id)
400
        : AggrNode(node, t)
401
        , prop_name(id)
402
    {
792✔
403
    }
792✔
404

405
protected:
406
    std::string prop_name;
407
    std::unique_ptr<Subexpr> visit(ParserDriver*, DataType) override;
408
};
409

410
class SubqueryNode : public ValueNode {
411
public:
412
    PropertyNode* prop = nullptr;
413
    std::string variable_name;
414
    QueryNode* subquery = nullptr;
415

416
    SubqueryNode(PropertyNode* node, std::string var_name, QueryNode* query)
417
        : prop(node)
418
        , variable_name(var_name)
419
        , subquery(query)
420
    {
324✔
421
    }
324✔
422
    std::unique_ptr<Subexpr> visit(ParserDriver*, DataType) override;
423
};
424

425
class OperationNode : public ExpressionNode {
426
public:
427
    ExpressionNode* m_left;
428
    ExpressionNode* m_right;
429
    char m_op;
430
    OperationNode(ExpressionNode* left, char op, ExpressionNode* right)
431
        : m_left(left)
432
        , m_right(right)
433
        , m_op(op)
434
    {
1,056✔
435
    }
1,056✔
436
    bool is_constant() final
437
    {
2,436✔
438
        return m_left->is_constant() && m_right->is_constant();
2,436✔
439
    }
2,436✔
440
    bool is_list() final
441
    {
1,380✔
442
        return m_left->is_list() || m_right->is_list();
1,380✔
443
    }
1,380✔
444
    std::unique_ptr<Subexpr> visit(ParserDriver*, DataType) override;
445
};
446

447
/******************************* Compare Nodes *******************************/
448

449
enum class CompareType : char {
450
    EQUAL,
451
    NOT_EQUAL,
452
    GREATER,
453
    LESS,
454
    GREATER_EQUAL,
455
    LESS_EQUAL,
456
    BEGINSWITH,
457
    ENDSWITH,
458
    CONTAINS,
459
    LIKE,
460
    IN,
461
    TEXT,
462
};
463

464
std::string_view string_for_op(CompareType op);
465

466
class CompareNode : public QueryNode {};
467

468
class EqualityNode : public CompareNode {
469
public:
470
    std::vector<ExpressionNode*> values;
471
    CompareType op;
472
    bool case_sensitive = true;
473

474
    EqualityNode(ExpressionNode* left, CompareType t, ExpressionNode* right)
475
        : op(t)
476
    {
464,806✔
477
        values.emplace_back(left);
464,806✔
478
        values.emplace_back(right);
464,806✔
479
    }
464,806✔
480
    Query visit(ParserDriver*) override;
481
};
482

483
class RelationalNode : public CompareNode {
484
public:
485
    std::vector<ExpressionNode*> values;
486
    CompareType op;
487

488
    RelationalNode(ExpressionNode* left, CompareType t, ExpressionNode* right)
489
        : op(t)
490
    {
5,276✔
491
        values.emplace_back(left);
5,276✔
492
        values.emplace_back(right);
5,276✔
493
    }
5,276✔
494
    Query visit(ParserDriver*) override;
495
};
496

497
class BetweenNode : public CompareNode {
498
public:
499
    ValueNode* prop;
500
    ListNode* limits;
501

502
    BetweenNode(ValueNode* left, ListNode* right)
503
        : prop(left)
504
        , limits(right)
505
    {
100✔
506
    }
100✔
507
    Query visit(ParserDriver*) override;
508
};
509

510
class StringOpsNode : public CompareNode {
511
public:
512
    std::vector<ExpressionNode*> values;
513
    CompareType op;
514
    bool case_sensitive = true;
515

516
    StringOpsNode(ValueNode* left, CompareType t, ValueNode* right)
517
        : op(t)
518
    {
2,476✔
519
        values.emplace_back(left);
2,476✔
520
        values.emplace_back(right);
2,476✔
521
    }
2,476✔
522
    Query visit(ParserDriver*) override;
523
};
524

525
class GeoWithinNode : public CompareNode {
526
public:
527
#if REALM_ENABLE_GEOSPATIAL
528
    PropertyNode* prop;
529
    GeospatialNode* geo = nullptr;
530
    std::string argument;
531
    GeoWithinNode(PropertyNode* left, GeospatialNode* right)
532
    {
140✔
533
        prop = left;
140✔
534
        geo = right;
140✔
535
    }
140✔
536
    GeoWithinNode(PropertyNode* left, std::string arg)
537
    {
48✔
538
        prop = left;
48✔
539
        argument = arg;
48✔
540
    }
48✔
541
    Query visit(ParserDriver*) override;
542
#else
543
    template <typename... Ts>
544
    GeoWithinNode(Ts&&...)
545
    {
546
        throw realm::LogicError(ErrorCodes::NotSupported, "Support for Geospatial queries is not enabled");
547
    }
548
    Query visit(ParserDriver*) override
549
    {
550
        return {};
551
    }
552
#endif
553
};
554

555
/******************************** Other Nodes ********************************/
556

557
class PostOpNode : public ParserNode {
558
public:
559
    enum OpType { SIZE, TYPE } op_type;
560
    std::string op_name;
561

562
    PostOpNode(std::string op_literal, OpType type)
563
        : op_type(type)
564
        , op_name(op_literal)
565
    {
4,308✔
566
    }
4,308✔
567
    std::unique_ptr<Subexpr> visit(ParserDriver*, Subexpr* subexpr);
568
};
569

570

571
class DescriptorNode : public ParserNode {
572
public:
573
    enum Type { SORT, DISTINCT, LIMIT };
574
    std::vector<Path> columns;
575
    std::vector<bool> ascending;
576
    size_t limit = size_t(-1);
577
    Type type;
578

579
    DescriptorNode(Type t)
580
        : type(t)
581
    {
1,000✔
582
    }
1,000✔
583
    DescriptorNode(Type t, const std::string& str)
584
        : type(t)
585
    {
404✔
586
        limit = size_t(strtol(str.c_str(), nullptr, 0));
404✔
587
    }
404✔
588
    ~DescriptorNode() override;
589
    Type get_type()
590
    {
1,940✔
591
        return type;
1,940✔
592
    }
1,940✔
593
    void add(PathNode* path)
594
    {
1,116✔
595
        auto& vec = columns.emplace_back();
1,116✔
596
        vec = std::move(path->path_elems);
1,116✔
597
    }
1,116✔
598
    void add(PathNode* path, bool direction)
599
    {
716✔
600
        add(path);
716✔
601
        ascending.push_back(direction);
716✔
602
    }
716✔
603
};
604

605
class DescriptorOrderingNode : public ParserNode {
606
public:
607
    std::vector<DescriptorNode*> orderings;
608

609
    DescriptorOrderingNode() = default;
42,198✔
610
    ~DescriptorOrderingNode() override;
611
    void add_descriptor(DescriptorNode* n)
612
    {
1,388✔
613
        orderings.push_back(n);
1,388✔
614
    }
1,388✔
615
    std::unique_ptr<DescriptorOrdering> visit(ParserDriver* drv);
616
};
617

618
// Conducting the whole scanning and parsing of Calc++.
619
class ParserDriver {
620
public:
621
    using SubexprPtr = std::unique_ptr<Subexpr>;
622
    class ParserNodeStore {
623
    public:
624
        template <typename T, typename... Args>
625
        T* create(Args&&... args)
626
        {
2,385,648✔
627
            auto owned = std::make_unique<T>(std::forward<Args>(args)...);
2,385,648✔
628
            auto ret = owned.get();
2,385,648✔
629
            m_store.push_back(std::move(owned));
2,385,648✔
630
            return ret;
2,385,648✔
631
        }
2,385,648✔
632

633
    private:
634
        std::vector<std::unique_ptr<ParserNode>> m_store;
635
    };
636

637
    ParserDriver()
638
        : ParserDriver(TableRef(), s_default_args, s_default_mapping)
639
    {
1,024✔
640
    }
1,024✔
641

642
    ParserDriver(TableRef t, Arguments& args, const query_parser::KeyPathMapping& mapping);
643
    ~ParserDriver();
644

645
    util::serializer::SerialisationState m_serializer_state;
646
    QueryNode* result = nullptr;
647
    DescriptorOrderingNode* ordering = nullptr;
648
    TableRef m_base_table;
649
    Arguments& m_args;
650
    query_parser::KeyPathMapping m_mapping;
651
    ParserNodeStore m_parse_nodes;
652
    void* m_yyscanner;
653

654
    // Run the parser on file F.  Return 0 on success.
655
    int parse(const std::string& str);
656

657
    // Handling the scanner.
658
    void scan_begin(void*, bool trace_scanning);
659

660
    void error(const std::string& err)
661
    {
724✔
662
        error_string = err;
724✔
663
        parse_error = true;
724✔
664
    }
724✔
665

666
    PathElement get_arg_for_index(const std::string&);
667
    double get_arg_for_coordinate(const std::string&);
668

669
    template <class T>
670
    Query simple_query(CompareType op, ColKey col_key, T val, bool case_sensitive);
671
    template <class T>
672
    Query simple_query(CompareType op, ColKey col_key, T val);
673
    std::pair<SubexprPtr, SubexprPtr> cmp(const std::vector<ExpressionNode*>& values);
674
    SubexprPtr column(LinkChain&, PathNode*);
675
    void backlink(LinkChain&, std::string_view table_name, std::string_view column_name);
676
    std::string translate(const LinkChain&, const std::string&);
677

678
private:
679
    // The string being parsed.
680
    std::string parse_buffer;
681
    std::string error_string;
682
    void* scan_buffer = nullptr;
683
    bool parse_error = false;
684

685
    static NoArguments s_default_args;
686
    static query_parser::KeyPathMapping s_default_mapping;
687
};
688

689
template <class T>
690
Query ParserDriver::simple_query(CompareType op, ColKey col_key, T val, bool case_sensitive)
691
{
3,924✔
692
    switch (op) {
3,924✔
693
        case CompareType::IN:
1,938✔
694
        case CompareType::EQUAL:
3,876✔
695
            return m_base_table->where().equal(col_key, val, case_sensitive);
3,876✔
696
        case CompareType::NOT_EQUAL:
1,962✔
697
            return m_base_table->where().not_equal(col_key, val, case_sensitive);
48✔
698
        default:
1,938✔
699
            break;
×
700
    }
×
701
    return m_base_table->where();
×
702
}
×
703

704
template <class T>
705
Query ParserDriver::simple_query(CompareType op, ColKey col_key, T val)
706
{
440,920✔
707
    switch (op) {
440,920✔
708
        case CompareType::IN:
219,648✔
709
        case CompareType::EQUAL:
439,296✔
710
            return m_base_table->where().equal(col_key, val);
439,296✔
711
        case CompareType::NOT_EQUAL:
219,728✔
712
            return m_base_table->where().not_equal(col_key, val);
160✔
713
        case CompareType::GREATER:
220,002✔
714
            return m_base_table->where().greater(col_key, val);
708✔
715
        case CompareType::LESS:
219,806✔
716
            return m_base_table->where().less(col_key, val);
316✔
717
        case CompareType::GREATER_EQUAL:
219,768✔
718
            return m_base_table->where().greater_equal(col_key, val);
240✔
719
        case CompareType::LESS_EQUAL:
219,748✔
720
            return m_base_table->where().less_equal(col_key, val);
200✔
721
        default:
219,648✔
722
            break;
×
723
    }
×
724
    return m_base_table->where();
×
725
}
×
726

727
std::string check_escapes(const char* str);
728

729
} // namespace query_parser
730
} // namespace realm
731
#endif // ! DRIVER_HH
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc