diff --git a/src/realm/object-store/results.cpp b/src/realm/object-store/results.cpp index c62ce500948..60940b4c998 100644 --- a/src/realm/object-store/results.cpp +++ b/src/realm/object-store/results.cpp @@ -26,7 +26,7 @@ #include #include #include - +#include #include #include @@ -56,6 +56,16 @@ Results::Results(SharedRealm r, Query q, DescriptorOrdering o) { } +Results::Results(SharedRealm r, ConstTableRef table, const bson::BsonDocument& document) + : Results(r, table->query(document)) +{ +} + +Results::Results(SharedRealm r, ConstTableRef table, const std::string& document) + : Results(r, table->query(static_cast(bson::parse(document)))) +{ +} + Results::Results(const Class& cls) : Results(cls.get_realm(), cls.get_table()) { diff --git a/src/realm/object-store/results.hpp b/src/realm/object-store/results.hpp index 3a45a76d924..c768c6ce417 100644 --- a/src/realm/object-store/results.hpp +++ b/src/realm/object-store/results.hpp @@ -51,6 +51,8 @@ class Results { Results(); Results(const Class&); Results(std::shared_ptr r, ConstTableRef table); + Results(std::shared_ptr r, ConstTableRef, const bson::BsonDocument& document); + Results(std::shared_ptr r, ConstTableRef, const std::string& document); Results(std::shared_ptr r, Query q, DescriptorOrdering o = {}); Results(std::shared_ptr r, TableView tv, DescriptorOrdering o = {}); Results(std::shared_ptr r, const Obj& obj, TableKey src_table, ColKey src_col_key) @@ -152,6 +154,9 @@ class Results { // Create a new Results by further filtering or sorting this Results Results filter(Query&& q) const REQUIRES(!m_mutex); + Results find(const bson::BsonDocument& document) const REQUIRES(!m_mutex); + Results find(const std::string& document) const REQUIRES(!m_mutex); + // Create a new Results by sorting this Result. Results sort(SortDescriptor&& sort) const REQUIRES(!m_mutex); // Create a new Results by sorting this Result based on the specified key paths. diff --git a/src/realm/parser/CMakeLists.txt b/src/realm/parser/CMakeLists.txt index 12dda0ada14..044430d4692 100644 --- a/src/realm/parser/CMakeLists.txt +++ b/src/realm/parser/CMakeLists.txt @@ -31,7 +31,8 @@ if(NOT REALM_CORE_SUBMODULE_BUILD) endif() set(REALM_PARSER_SOURCES - driver.cpp + query_ast.cpp + query_bson.cpp keypath_mapping.cpp ) # REALM_PARSER_SOURCES @@ -41,7 +42,7 @@ set(REALM_PARSER_GENERATED ) # REALM_PARSER_SOURCES set(REALM_PARSER_HEADERS - driver.hpp + query_ast.hpp keypath_mapping.hpp query_parser.hpp generated/query_bison.hpp diff --git a/src/realm/parser/generated/query_bison.cpp b/src/realm/parser/generated/query_bison.cpp index 8764fada001..ff669db5002 100644 --- a/src/realm/parser/generated/query_bison.cpp +++ b/src/realm/parser/generated/query_bison.cpp @@ -43,7 +43,7 @@ // Unqualified %code blocks. -#include +#include #include using namespace realm; using namespace realm::query_parser; @@ -204,11 +204,6 @@ namespace yy { value.YY_MOVE_OR_COPY< CompareType > (YY_MOVE (that.value)); break; - case symbol_kind::SYM_constant: // constant - case symbol_kind::SYM_primary_key: // primary_key - value.YY_MOVE_OR_COPY< ConstantNode* > (YY_MOVE (that.value)); - break; - case symbol_kind::SYM_distinct: // distinct case symbol_kind::SYM_distinct_param: // distinct_param case symbol_kind::SYM_sort: // sort @@ -259,6 +254,11 @@ namespace yy { value.YY_MOVE_OR_COPY< QueryNode* > (YY_MOVE (that.value)); break; + case symbol_kind::SYM_constant: // constant + case symbol_kind::SYM_primary_key: // primary_key + value.YY_MOVE_OR_COPY< StringConstantNode* > (YY_MOVE (that.value)); + break; + case symbol_kind::SYM_subquery: // subquery value.YY_MOVE_OR_COPY< SubqueryNode* > (YY_MOVE (that.value)); break; @@ -348,11 +348,6 @@ namespace yy { value.move< CompareType > (YY_MOVE (that.value)); break; - case symbol_kind::SYM_constant: // constant - case symbol_kind::SYM_primary_key: // primary_key - value.move< ConstantNode* > (YY_MOVE (that.value)); - break; - case symbol_kind::SYM_distinct: // distinct case symbol_kind::SYM_distinct_param: // distinct_param case symbol_kind::SYM_sort: // sort @@ -403,6 +398,11 @@ namespace yy { value.move< QueryNode* > (YY_MOVE (that.value)); break; + case symbol_kind::SYM_constant: // constant + case symbol_kind::SYM_primary_key: // primary_key + value.move< StringConstantNode* > (YY_MOVE (that.value)); + break; + case symbol_kind::SYM_subquery: // subquery value.move< SubqueryNode* > (YY_MOVE (that.value)); break; @@ -492,11 +492,6 @@ namespace yy { value.copy< CompareType > (that.value); break; - case symbol_kind::SYM_constant: // constant - case symbol_kind::SYM_primary_key: // primary_key - value.copy< ConstantNode* > (that.value); - break; - case symbol_kind::SYM_distinct: // distinct case symbol_kind::SYM_distinct_param: // distinct_param case symbol_kind::SYM_sort: // sort @@ -547,6 +542,11 @@ namespace yy { value.copy< QueryNode* > (that.value); break; + case symbol_kind::SYM_constant: // constant + case symbol_kind::SYM_primary_key: // primary_key + value.copy< StringConstantNode* > (that.value); + break; + case symbol_kind::SYM_subquery: // subquery value.copy< SubqueryNode* > (that.value); break; @@ -634,11 +634,6 @@ namespace yy { value.move< CompareType > (that.value); break; - case symbol_kind::SYM_constant: // constant - case symbol_kind::SYM_primary_key: // primary_key - value.move< ConstantNode* > (that.value); - break; - case symbol_kind::SYM_distinct: // distinct case symbol_kind::SYM_distinct_param: // distinct_param case symbol_kind::SYM_sort: // sort @@ -689,6 +684,11 @@ namespace yy { value.move< QueryNode* > (that.value); break; + case symbol_kind::SYM_constant: // constant + case symbol_kind::SYM_primary_key: // primary_key + value.move< StringConstantNode* > (that.value); + break; + case symbol_kind::SYM_subquery: // subquery value.move< SubqueryNode* > (that.value); break; @@ -1175,11 +1175,11 @@ namespace yy { break; case symbol_kind::SYM_constant: // constant - { yyo << yysym.value.template as < ConstantNode* > (); } + { yyo << yysym.value.template as < StringConstantNode* > (); } break; case symbol_kind::SYM_primary_key: // primary_key - { yyo << yysym.value.template as < ConstantNode* > (); } + { yyo << yysym.value.template as < StringConstantNode* > (); } break; case symbol_kind::SYM_boolexpr: // boolexpr @@ -1456,11 +1456,6 @@ namespace yy { yylhs.value.emplace< CompareType > (); break; - case symbol_kind::SYM_constant: // constant - case symbol_kind::SYM_primary_key: // primary_key - yylhs.value.emplace< ConstantNode* > (); - break; - case symbol_kind::SYM_distinct: // distinct case symbol_kind::SYM_distinct_param: // distinct_param case symbol_kind::SYM_sort: // sort @@ -1511,6 +1506,11 @@ namespace yy { yylhs.value.emplace< QueryNode* > (); break; + case symbol_kind::SYM_constant: // constant + case symbol_kind::SYM_primary_key: // primary_key + yylhs.value.emplace< StringConstantNode* > (); + break; + case symbol_kind::SYM_subquery: // subquery yylhs.value.emplace< SubqueryNode* > (); break; @@ -1686,7 +1686,7 @@ namespace yy { break; case 24: // value: constant - { yylhs.value.as < ValueNode* > () = yystack_[0].value.as < ConstantNode* > ();} + { yylhs.value.as < ValueNode* > () = yystack_[0].value.as < StringConstantNode* > ();} break; case 25: // value: prop @@ -1848,7 +1848,7 @@ namespace yy { break; case 63: // list_content: constant - { yylhs.value.as < ListNode* > () = drv.m_parse_nodes.create(yystack_[0].value.as < ConstantNode* > ()); } + { yylhs.value.as < ListNode* > () = drv.m_parse_nodes.create(yystack_[0].value.as < StringConstantNode* > ()); } break; case 64: // list_content: %empty @@ -1856,87 +1856,87 @@ namespace yy { break; case 65: // list_content: list_content ',' constant - { yystack_[2].value.as < ListNode* > ()->add_element(yystack_[0].value.as < ConstantNode* > ()); yylhs.value.as < ListNode* > () = yystack_[2].value.as < ListNode* > (); } + { yystack_[2].value.as < ListNode* > ()->add_element(yystack_[0].value.as < StringConstantNode* > ()); yylhs.value.as < ListNode* > () = yystack_[2].value.as < ListNode* > (); } break; case 66: // constant: primary_key - { yylhs.value.as < ConstantNode* > () = yystack_[0].value.as < ConstantNode* > (); } + { yylhs.value.as < StringConstantNode* > () = yystack_[0].value.as < StringConstantNode* > (); } break; case 67: // constant: "infinity" - { yylhs.value.as < ConstantNode* > () = drv.m_parse_nodes.create(ConstantNode::INFINITY_VAL, yystack_[0].value.as < std::string > ()); } + { yylhs.value.as < StringConstantNode* > () = drv.m_parse_nodes.create(StringConstantNode::INFINITY_VAL, yystack_[0].value.as < std::string > ()); } break; case 68: // constant: "NaN" - { yylhs.value.as < ConstantNode* > () = drv.m_parse_nodes.create(ConstantNode::NAN_VAL, yystack_[0].value.as < std::string > ()); } + { yylhs.value.as < StringConstantNode* > () = drv.m_parse_nodes.create(StringConstantNode::NAN_VAL, yystack_[0].value.as < std::string > ()); } break; case 69: // constant: "base64" - { yylhs.value.as < ConstantNode* > () = drv.m_parse_nodes.create(ConstantNode::BASE64, yystack_[0].value.as < std::string > ()); } + { yylhs.value.as < StringConstantNode* > () = drv.m_parse_nodes.create(StringConstantNode::BASE64, yystack_[0].value.as < std::string > ()); } break; case 70: // constant: "float" - { yylhs.value.as < ConstantNode* > () = drv.m_parse_nodes.create(ConstantNode::FLOAT, yystack_[0].value.as < std::string > ()); } + { yylhs.value.as < StringConstantNode* > () = drv.m_parse_nodes.create(StringConstantNode::FLOAT, yystack_[0].value.as < std::string > ()); } break; case 71: // constant: "date" - { yylhs.value.as < ConstantNode* > () = drv.m_parse_nodes.create(ConstantNode::TIMESTAMP, yystack_[0].value.as < std::string > ()); } + { yylhs.value.as < StringConstantNode* > () = drv.m_parse_nodes.create(StringConstantNode::TIMESTAMP, yystack_[0].value.as < std::string > ()); } break; case 72: // constant: "link" - { yylhs.value.as < ConstantNode* > () = drv.m_parse_nodes.create(ConstantNode::LINK, yystack_[0].value.as < std::string > ()); } + { yylhs.value.as < StringConstantNode* > () = drv.m_parse_nodes.create(StringConstantNode::LINK, yystack_[0].value.as < std::string > ()); } break; case 73: // constant: "typed link" - { yylhs.value.as < ConstantNode* > () = drv.m_parse_nodes.create(ConstantNode::TYPED_LINK, yystack_[0].value.as < std::string > ()); } + { yylhs.value.as < StringConstantNode* > () = drv.m_parse_nodes.create(StringConstantNode::TYPED_LINK, yystack_[0].value.as < std::string > ()); } break; case 74: // constant: "true" - { yylhs.value.as < ConstantNode* > () = drv.m_parse_nodes.create(ConstantNode::TRUE, ""); } + { yylhs.value.as < StringConstantNode* > () = drv.m_parse_nodes.create(StringConstantNode::TRUE, ""); } break; case 75: // constant: "false" - { yylhs.value.as < ConstantNode* > () = drv.m_parse_nodes.create(ConstantNode::FALSE, ""); } + { yylhs.value.as < StringConstantNode* > () = drv.m_parse_nodes.create(StringConstantNode::FALSE, ""); } break; case 76: // constant: "null" - { yylhs.value.as < ConstantNode* > () = drv.m_parse_nodes.create(ConstantNode::NULL_VAL, ""); } + { yylhs.value.as < StringConstantNode* > () = drv.m_parse_nodes.create(StringConstantNode::NULL_VAL, ""); } break; case 77: // constant: "argument" - { yylhs.value.as < ConstantNode* > () = drv.m_parse_nodes.create(ConstantNode::ARG, yystack_[0].value.as < std::string > ()); } + { yylhs.value.as < StringConstantNode* > () = drv.m_parse_nodes.create(StringConstantNode::ARG, yystack_[0].value.as < std::string > ()); } break; case 78: // constant: comp_type "argument" - { yylhs.value.as < ConstantNode* > () = drv.m_parse_nodes.create(ExpressionComparisonType(yystack_[1].value.as < int > ()), yystack_[0].value.as < std::string > ()); } + { yylhs.value.as < StringConstantNode* > () = drv.m_parse_nodes.create(ExpressionComparisonType(yystack_[1].value.as < int > ()), yystack_[0].value.as < std::string > ()); } break; case 79: // constant: "obj" '(' "string" ',' primary_key ')' { - auto tmp = yystack_[1].value.as < ConstantNode* > (); + auto tmp = yystack_[1].value.as < StringConstantNode* > (); tmp->add_table(yystack_[3].value.as < std::string > ()); - yylhs.value.as < ConstantNode* > () = tmp; + yylhs.value.as < StringConstantNode* > () = tmp; } break; case 80: // primary_key: "natural0" - { yylhs.value.as < ConstantNode* > () = drv.m_parse_nodes.create(ConstantNode::NUMBER, yystack_[0].value.as < std::string > ()); } + { yylhs.value.as < StringConstantNode* > () = drv.m_parse_nodes.create(StringConstantNode::NUMBER, yystack_[0].value.as < std::string > ()); } break; case 81: // primary_key: "number" - { yylhs.value.as < ConstantNode* > () = drv.m_parse_nodes.create(ConstantNode::NUMBER, yystack_[0].value.as < std::string > ()); } + { yylhs.value.as < StringConstantNode* > () = drv.m_parse_nodes.create(StringConstantNode::NUMBER, yystack_[0].value.as < std::string > ()); } break; case 82: // primary_key: "string" - { yylhs.value.as < ConstantNode* > () = drv.m_parse_nodes.create(ConstantNode::STRING, yystack_[0].value.as < std::string > ()); } + { yylhs.value.as < StringConstantNode* > () = drv.m_parse_nodes.create(StringConstantNode::STRING, yystack_[0].value.as < std::string > ()); } break; case 83: // primary_key: "UUID" - { yylhs.value.as < ConstantNode* > () = drv.m_parse_nodes.create(ConstantNode::UUID_T, yystack_[0].value.as < std::string > ()); } + { yylhs.value.as < StringConstantNode* > () = drv.m_parse_nodes.create(StringConstantNode::UUID_T, yystack_[0].value.as < std::string > ()); } break; case 84: // primary_key: "ObjectId" - { yylhs.value.as < ConstantNode* > () = drv.m_parse_nodes.create(ConstantNode::OID, yystack_[0].value.as < std::string > ()); } + { yylhs.value.as < StringConstantNode* > () = drv.m_parse_nodes.create(StringConstantNode::OID, yystack_[0].value.as < std::string > ()); } break; case 85: // boolexpr: "truepredicate" diff --git a/src/realm/parser/generated/query_bison.hpp b/src/realm/parser/generated/query_bison.hpp index 8c5e39dbe46..8bdd5f86184 100644 --- a/src/realm/parser/generated/query_bison.hpp +++ b/src/realm/parser/generated/query_bison.hpp @@ -55,7 +55,7 @@ using realm::GeoPoint; namespace realm::query_parser { class ParserDriver; - class ConstantNode; + class StringConstantNode; class GeospatialNode; class ListNode; class PostOpNode; @@ -454,49 +454,49 @@ namespace yy { // stringop char dummy2[sizeof (CompareType)]; - // constant - // primary_key - char dummy3[sizeof (ConstantNode*)]; - // distinct // distinct_param // sort // sort_param // limit - char dummy4[sizeof (DescriptorNode*)]; + char dummy3[sizeof (DescriptorNode*)]; // post_query - char dummy5[sizeof (DescriptorOrderingNode*)]; + char dummy4[sizeof (DescriptorOrderingNode*)]; // expr - char dummy6[sizeof (ExpressionNode*)]; + char dummy5[sizeof (ExpressionNode*)]; // geoloop_content // geoloop // geopoly_content // geospatial - char dummy7[sizeof (GeospatialNode*)]; + char dummy6[sizeof (GeospatialNode*)]; // list // list_content - char dummy8[sizeof (ListNode*)]; + char dummy7[sizeof (ListNode*)]; // path_elem - char dummy9[sizeof (PathElem)]; + char dummy8[sizeof (PathElem)]; // path - char dummy10[sizeof (PathNode*)]; + char dummy9[sizeof (PathNode*)]; // post_op - char dummy11[sizeof (PostOpNode*)]; + char dummy10[sizeof (PostOpNode*)]; // prop // simple_prop - char dummy12[sizeof (PropertyNode*)]; + char dummy11[sizeof (PropertyNode*)]; // query // compare - char dummy13[sizeof (QueryNode*)]; + char dummy12[sizeof (QueryNode*)]; + + // constant + // primary_key + char dummy13[sizeof (StringConstantNode*)]; // subquery char dummy14[sizeof (SubqueryNode*)]; @@ -829,11 +829,6 @@ namespace yy { value.move< CompareType > (std::move (that.value)); break; - case symbol_kind::SYM_constant: // constant - case symbol_kind::SYM_primary_key: // primary_key - value.move< ConstantNode* > (std::move (that.value)); - break; - case symbol_kind::SYM_distinct: // distinct case symbol_kind::SYM_distinct_param: // distinct_param case symbol_kind::SYM_sort: // sort @@ -884,6 +879,11 @@ namespace yy { value.move< QueryNode* > (std::move (that.value)); break; + case symbol_kind::SYM_constant: // constant + case symbol_kind::SYM_primary_key: // primary_key + value.move< StringConstantNode* > (std::move (that.value)); + break; + case symbol_kind::SYM_subquery: // subquery value.move< SubqueryNode* > (std::move (that.value)); break; @@ -993,18 +993,6 @@ namespace yy { {} #endif -#if 201103L <= YY_CPLUSPLUS - basic_symbol (typename Base::kind_type t, ConstantNode*&& v) - : Base (t) - , value (std::move (v)) - {} -#else - basic_symbol (typename Base::kind_type t, const ConstantNode*& v) - : Base (t) - , value (v) - {} -#endif - #if 201103L <= YY_CPLUSPLUS basic_symbol (typename Base::kind_type t, DescriptorNode*&& v) : Base (t) @@ -1125,6 +1113,18 @@ namespace yy { {} #endif +#if 201103L <= YY_CPLUSPLUS + basic_symbol (typename Base::kind_type t, StringConstantNode*&& v) + : Base (t) + , value (std::move (v)) + {} +#else + basic_symbol (typename Base::kind_type t, const StringConstantNode*& v) + : Base (t) + , value (v) + {} +#endif + #if 201103L <= YY_CPLUSPLUS basic_symbol (typename Base::kind_type t, SubqueryNode*&& v) : Base (t) @@ -1263,11 +1263,6 @@ switch (yykind) value.template destroy< CompareType > (); break; - case symbol_kind::SYM_constant: // constant - case symbol_kind::SYM_primary_key: // primary_key - value.template destroy< ConstantNode* > (); - break; - case symbol_kind::SYM_distinct: // distinct case symbol_kind::SYM_distinct_param: // distinct_param case symbol_kind::SYM_sort: // sort @@ -1318,6 +1313,11 @@ switch (yykind) value.template destroy< QueryNode* > (); break; + case symbol_kind::SYM_constant: // constant + case symbol_kind::SYM_primary_key: // primary_key + value.template destroy< StringConstantNode* > (); + break; + case symbol_kind::SYM_subquery: // subquery value.template destroy< SubqueryNode* > (); break; @@ -2877,11 +2877,6 @@ switch (yykind) value.copy< CompareType > (YY_MOVE (that.value)); break; - case symbol_kind::SYM_constant: // constant - case symbol_kind::SYM_primary_key: // primary_key - value.copy< ConstantNode* > (YY_MOVE (that.value)); - break; - case symbol_kind::SYM_distinct: // distinct case symbol_kind::SYM_distinct_param: // distinct_param case symbol_kind::SYM_sort: // sort @@ -2932,6 +2927,11 @@ switch (yykind) value.copy< QueryNode* > (YY_MOVE (that.value)); break; + case symbol_kind::SYM_constant: // constant + case symbol_kind::SYM_primary_key: // primary_key + value.copy< StringConstantNode* > (YY_MOVE (that.value)); + break; + case symbol_kind::SYM_subquery: // subquery value.copy< SubqueryNode* > (YY_MOVE (that.value)); break; @@ -3037,11 +3037,6 @@ switch (yykind) value.move< CompareType > (YY_MOVE (s.value)); break; - case symbol_kind::SYM_constant: // constant - case symbol_kind::SYM_primary_key: // primary_key - value.move< ConstantNode* > (YY_MOVE (s.value)); - break; - case symbol_kind::SYM_distinct: // distinct case symbol_kind::SYM_distinct_param: // distinct_param case symbol_kind::SYM_sort: // sort @@ -3092,6 +3087,11 @@ switch (yykind) value.move< QueryNode* > (YY_MOVE (s.value)); break; + case symbol_kind::SYM_constant: // constant + case symbol_kind::SYM_primary_key: // primary_key + value.move< StringConstantNode* > (YY_MOVE (s.value)); + break; + case symbol_kind::SYM_subquery: // subquery value.move< SubqueryNode* > (YY_MOVE (s.value)); break; diff --git a/src/realm/parser/generated/query_flex.cpp b/src/realm/parser/generated/query_flex.cpp index a00bb565312..25d7f0f0055 100644 --- a/src/realm/parser/generated/query_flex.cpp +++ b/src/realm/parser/generated/query_flex.cpp @@ -1195,7 +1195,7 @@ static const flex_int16_t yy_rule_linenum[68] = # include # include // strerror # include -# include "realm/parser/driver.hpp" +# include "realm/parser/query_ast.hpp" #define YY_NO_UNISTD_H 1 #define YY_NO_INPUT 1 diff --git a/src/realm/parser/driver.cpp b/src/realm/parser/query_ast.cpp similarity index 99% rename from src/realm/parser/driver.cpp rename to src/realm/parser/query_ast.cpp index 62505d6bff1..77c5812a732 100644 --- a/src/realm/parser/driver.cpp +++ b/src/realm/parser/query_ast.cpp @@ -16,7 +16,7 @@ * **************************************************************************/ -#include "realm/parser/driver.hpp" +#include "realm/parser/query_ast.hpp" #include "realm/parser/keypath_mapping.hpp" #include "realm/parser/query_parser.hpp" #include "realm/sort_descriptor.hpp" @@ -1158,7 +1158,7 @@ std::unique_ptr AggrNode::aggregate(Subexpr* subexpr) return agg; } -std::unique_ptr ConstantNode::visit(ParserDriver* drv, DataType hint) +std::unique_ptr StringConstantNode::visit(ParserDriver* drv, DataType hint) { std::unique_ptr ret; std::string explain_value_message = text; @@ -1399,7 +1399,7 @@ std::unique_ptr ConstantNode::visit(ParserDriver* drv, DataType hint) return ret; } -std::unique_ptr ConstantNode::copy_list_of_args(std::vector& mixed_args) +std::unique_ptr StringConstantNode::copy_list_of_args(std::vector& mixed_args) { std::unique_ptr args_in_list = std::make_unique(mixed_args.size()); size_t ndx = 0; @@ -1412,8 +1412,8 @@ std::unique_ptr ConstantNode::copy_list_of_args(std::vector ConstantNode::copy_arg(ParserDriver* drv, DataType type, size_t arg_no, DataType hint, - std::string& err) +std::unique_ptr StringConstantNode::copy_arg(ParserDriver* drv, DataType type, size_t arg_no, DataType hint, + std::string& err) { switch (type) { case type_Int: diff --git a/src/realm/parser/driver.hpp b/src/realm/parser/query_ast.hpp similarity index 96% rename from src/realm/parser/driver.hpp rename to src/realm/parser/query_ast.hpp index 83dd6a297ab..fa3d4b3b675 100644 --- a/src/realm/parser/driver.hpp +++ b/src/realm/parser/query_ast.hpp @@ -53,6 +53,10 @@ class LogicalNode : public QueryNode { children.emplace_back(left); children.emplace_back(right); } + LogicalNode(std::vector&& vec) + : children(std::move(vec)) + { + } void canonicalize() override { std::vector todo; @@ -145,10 +149,11 @@ class ExpressionNode : public ParserNode { /******************************** Value Nodes ********************************/ -class ValueNode : public ExpressionNode { -}; +class ValueNode : public ExpressionNode {}; -class ConstantNode : public ValueNode { +class ConstantNode : public ValueNode {}; + +class StringConstantNode : public ConstantNode { public: enum Type { NUMBER, @@ -172,12 +177,12 @@ class ConstantNode : public ValueNode { std::string text; - ConstantNode(Type t, const std::string& str) + StringConstantNode(Type t, const std::string& str) : type(t) , text(str) { } - ConstantNode(ExpressionComparisonType comp_type, const std::string& str) + StringConstantNode(ExpressionComparisonType comp_type, const std::string& str) : type(Type::ARG) , text(str) , m_comp_type(comp_type) @@ -199,7 +204,7 @@ class ConstantNode : public ValueNode { std::string target_table; }; -class GeospatialNode : public ValueNode { +class GeospatialNode : public ConstantNode { public: struct Box {}; struct Polygon {}; @@ -275,6 +280,7 @@ class PathNode : public ParserNode { public: std::vector path_elems; + PathNode() {} PathNode(PathElem first) { add_element(first); @@ -618,6 +624,8 @@ class ParserDriver { // Run the parser on file F. Return 0 on success. int parse(const std::string& str); + void parse(const bson::BsonDocument& document); + // Handling the scanner. void scan_begin(void*, bool trace_scanning); @@ -649,6 +657,9 @@ class ParserDriver { static NoArguments s_default_args; static query_parser::KeyPathMapping s_default_mapping; + + std::vector get_query_nodes(const bson::BsonArray& bson_array); + QueryNode* get_query_node(const bson::BsonDocument& document); }; template diff --git a/src/realm/parser/query_bison.yy b/src/realm/parser/query_bison.yy index 7fdc2742793..091634bc5c6 100644 --- a/src/realm/parser/query_bison.yy +++ b/src/realm/parser/query_bison.yy @@ -17,7 +17,7 @@ using realm::GeoPoint; namespace realm::query_parser { class ParserDriver; - class ConstantNode; + class StringConstantNode; class GeospatialNode; class ListNode; class PostOpNode; @@ -62,7 +62,7 @@ %define parse.error verbose %code { -#include +#include #include using namespace realm; using namespace realm::query_parser; @@ -141,7 +141,7 @@ using namespace realm::query_parser; %type equality relational stringop %type aggr_op %type coordinate -%type constant primary_key +%type constant primary_key %type geospatial geoloop geoloop_content geopoly_content // std::optional is necessary because GeoPoint has deleted its default constructor // but bison must push a default value to the stack, even though it will be overwritten by a real value @@ -310,18 +310,18 @@ list_content constant : primary_key { $$ = $1; } - | INFINITY { $$ = drv.m_parse_nodes.create(ConstantNode::INFINITY_VAL, $1); } - | NAN { $$ = drv.m_parse_nodes.create(ConstantNode::NAN_VAL, $1); } - | BASE64 { $$ = drv.m_parse_nodes.create(ConstantNode::BASE64, $1); } - | FLOAT { $$ = drv.m_parse_nodes.create(ConstantNode::FLOAT, $1); } - | TIMESTAMP { $$ = drv.m_parse_nodes.create(ConstantNode::TIMESTAMP, $1); } - | LINK { $$ = drv.m_parse_nodes.create(ConstantNode::LINK, $1); } - | TYPED_LINK { $$ = drv.m_parse_nodes.create(ConstantNode::TYPED_LINK, $1); } - | TRUE { $$ = drv.m_parse_nodes.create(ConstantNode::TRUE, ""); } - | FALSE { $$ = drv.m_parse_nodes.create(ConstantNode::FALSE, ""); } - | NULL_VAL { $$ = drv.m_parse_nodes.create(ConstantNode::NULL_VAL, ""); } - | ARG { $$ = drv.m_parse_nodes.create(ConstantNode::ARG, $1); } - | comp_type ARG { $$ = drv.m_parse_nodes.create(ExpressionComparisonType($1), $2); } + | INFINITY { $$ = drv.m_parse_nodes.create(StringConstantNode::INFINITY_VAL, $1); } + | NAN { $$ = drv.m_parse_nodes.create(StringConstantNode::NAN_VAL, $1); } + | BASE64 { $$ = drv.m_parse_nodes.create(StringConstantNode::BASE64, $1); } + | FLOAT { $$ = drv.m_parse_nodes.create(StringConstantNode::FLOAT, $1); } + | TIMESTAMP { $$ = drv.m_parse_nodes.create(StringConstantNode::TIMESTAMP, $1); } + | LINK { $$ = drv.m_parse_nodes.create(StringConstantNode::LINK, $1); } + | TYPED_LINK { $$ = drv.m_parse_nodes.create(StringConstantNode::TYPED_LINK, $1); } + | TRUE { $$ = drv.m_parse_nodes.create(StringConstantNode::TRUE, ""); } + | FALSE { $$ = drv.m_parse_nodes.create(StringConstantNode::FALSE, ""); } + | NULL_VAL { $$ = drv.m_parse_nodes.create(StringConstantNode::NULL_VAL, ""); } + | ARG { $$ = drv.m_parse_nodes.create(StringConstantNode::ARG, $1); } + | comp_type ARG { $$ = drv.m_parse_nodes.create(ExpressionComparisonType($1), $2); } | OBJ '(' STRING ',' primary_key ')' { auto tmp = $5; @@ -330,11 +330,11 @@ constant } primary_key - : NATURAL0 { $$ = drv.m_parse_nodes.create(ConstantNode::NUMBER, $1); } - | NUMBER { $$ = drv.m_parse_nodes.create(ConstantNode::NUMBER, $1); } - | STRING { $$ = drv.m_parse_nodes.create(ConstantNode::STRING, $1); } - | UUID { $$ = drv.m_parse_nodes.create(ConstantNode::UUID_T, $1); } - | OID { $$ = drv.m_parse_nodes.create(ConstantNode::OID, $1); } + : NATURAL0 { $$ = drv.m_parse_nodes.create(StringConstantNode::NUMBER, $1); } + | NUMBER { $$ = drv.m_parse_nodes.create(StringConstantNode::NUMBER, $1); } + | STRING { $$ = drv.m_parse_nodes.create(StringConstantNode::STRING, $1); } + | UUID { $$ = drv.m_parse_nodes.create(StringConstantNode::UUID_T, $1); } + | OID { $$ = drv.m_parse_nodes.create(StringConstantNode::OID, $1); } boolexpr : "truepredicate" { $$ = drv.m_parse_nodes.create(true); } diff --git a/src/realm/parser/query_bson.cpp b/src/realm/parser/query_bson.cpp new file mode 100644 index 00000000000..89ee88fdcc9 --- /dev/null +++ b/src/realm/parser/query_bson.cpp @@ -0,0 +1,237 @@ +/************************************************************************* + * + * Copyright 2023 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#include +#include +#include +#include "query_ast.hpp" + +namespace realm { + +using namespace query_parser; + +namespace { + +std::map equal_operators = { + {"$eq", CompareType::EQUAL}, + {"$in", CompareType::IN}, + {"$ne", CompareType::NOT_EQUAL}, +}; + +std::map relational_operators = { + {"$gt", CompareType::GREATER}, + {"$gte", CompareType::GREATER_EQUAL}, + {"$lt", CompareType::LESS}, + {"$lte", CompareType::LESS_EQUAL}, +}; + +enum class QueryLogicalOperators { $and, $not, $nor, $or }; + +std::map logical_operators = { + {"$and", QueryLogicalOperators::$and}, + {"$or", QueryLogicalOperators::$or}, + {"$nor", QueryLogicalOperators::$nor}, + {"$not", QueryLogicalOperators::$not}, +}; + +class BsonConstant : public ConstantNode { +public: + BsonConstant(bson::Bson b) + : value(b) + { + } + + std::unique_ptr visit(ParserDriver*, DataType) override + { + switch (value.type()) { + case bson::Bson::Type::Int32: + return std::make_unique>(int32_t(value)); + case bson::Bson::Type::Int64: + return std::make_unique>(int64_t(value)); + case bson::Bson::Type::Bool: + return std::make_unique>(bool(value)); + case bson::Bson::Type::Double: + return std::make_unique>(double(value)); + case bson::Bson::Type::String: + return std::make_unique(std::string(value)); + case bson::Bson::Type::Binary: { + auto data = static_cast>(value); + return std::make_unique(BinaryData(data.data(), data.size())); + } + case bson::Bson::Type::Timestamp: + case bson::Bson::Type::Datetime: + return std::make_unique>(Timestamp(value)); + case bson::Bson::Type::ObjectId: + return std::make_unique>(ObjectId(value)); + case bson::Bson::Type::Decimal128: + return std::make_unique>(Decimal128(value)); + case bson::Bson::Type::Uuid: + return std::make_unique>(UUID(value)); + case bson::Bson::Type::Null: + return std::make_unique>(realm::null()); + default: + throw query_parser::InvalidQueryError("Unsupported Bson Type"); + } + return {}; + } + +private: + bson::Bson value; +}; + +static inline const char* find_chr(const char* p, char c) +{ + while (*p && *p != c) { + ++p; + } + return p; +} + +static std::vector split(const char* path) +{ + std::vector ret; + do { + auto p = find_chr(path, '.'); + ret.emplace_back(path, p); + path = p; + } while (*path++ == '.'); + return ret; +} + +} // namespace + +std::vector ParserDriver::get_query_nodes(const bson::BsonArray& bson_array) +{ + std::vector ret; + for (const auto& document : bson_array) { + ret.emplace_back(get_query_node(static_cast(document))); + } + return ret; +} + +void ParserDriver::parse(const bson::BsonDocument& document) +{ + result = get_query_node(document); +} + +QueryNode* ParserDriver::get_query_node(const bson::BsonDocument& document) +{ + QueryNode* ret = nullptr; + for (const auto& [key, value] : document) { + QueryNode* node = nullptr; + // top level document will contain either keys to compare values against, + // or logical operators like $and or $or that will contain an array of query ops + if (logical_operators.count(key)) { + switch (logical_operators[key]) { + case QueryLogicalOperators::$and: { + if (value.type() != bson::Bson::Type::Array) + throw InvalidQueryError("Array expected"); + node = m_parse_nodes.create(get_query_nodes(static_cast(value))); + break; + } + case QueryLogicalOperators::$not: + if (value.type() != bson::Bson::Type::Document) + throw InvalidQueryError("Array expected"); + node = m_parse_nodes.create(get_query_node(static_cast(value))); + break; + case QueryLogicalOperators::$nor: + throw InvalidQueryError("$nor not supported"); + break; + case QueryLogicalOperators::$or: { + if (value.type() != bson::Bson::Type::Array) + throw InvalidQueryError("Array expected"); + node = m_parse_nodes.create(get_query_nodes(static_cast(value))); + break; + } + } + } + else { + auto path = m_parse_nodes.create(); + auto path_elements = split(key.c_str()); + for (auto& elem : path_elements) { + path->add_element(elem); + } + auto prop = m_parse_nodes.create(path); + // if the value type is a document, we expect that it is a + // "query document". if it's not, we assume they are doing a value comparison + if (value.type() == bson::Bson::Type::Document) { + const auto& doc = static_cast(value); + auto [key, val] = doc[0]; + ValueNode* right; + if (val.type() == bson::Bson::Type::Array) { + auto list = m_parse_nodes.create(); + for (const auto& document : static_cast(val)) { + list->add_element(m_parse_nodes.create(document)); + } + right = list; + } + else { + right = m_parse_nodes.create(val); + } + if (equal_operators.count(key)) { + node = m_parse_nodes.create(prop, equal_operators[key], right); + } + else { + node = m_parse_nodes.create(prop, relational_operators[key], right); + } + } + else { + node = m_parse_nodes.create(prop, CompareType::EQUAL, + m_parse_nodes.create(value)); + } + } + if (ret) { + ret = m_parse_nodes.create(ret, node); + } + else { + ret = node; + } + } + return ret; +} + +Query Table::query(const bson::BsonDocument& document) const +{ + NoArguments args; + ParserDriver driver(m_own_ref, args, {}); + driver.parse(document); + driver.result->canonicalize(); + return driver.result->visit(&driver); +} + +Results Results::find(const bson::BsonDocument& document) const +{ + return filter(m_table->query(document)); +} + +Results Results::find(const std::string& document) const +{ + const char* p = document.c_str(); + while (isspace(*p)) + ++p; + if (*p == '{') { + // This seems to be MQL + return find(static_cast(bson::parse(document))); + } + else { + // Try good old RQL + return filter(m_table->query(document)); + } +} + +} // namespace realm diff --git a/src/realm/parser/query_flex.ll b/src/realm/parser/query_flex.ll index 313d0c10ae0..db59f8bc813 100644 --- a/src/realm/parser/query_flex.ll +++ b/src/realm/parser/query_flex.ll @@ -7,7 +7,7 @@ # include # include // strerror # include -# include "realm/parser/driver.hpp" +# include "realm/parser/query_ast.hpp" %} %option nounistd never-interactive noyywrap nounput noinput batch debug noyylineno reentrant diff --git a/src/realm/table.hpp b/src/realm/table.hpp index 9e84b645abc..dddb15e1e82 100644 --- a/src/realm/table.hpp +++ b/src/realm/table.hpp @@ -79,6 +79,14 @@ class KeyPathMapping; class ParserDriver; } // namespace query_parser +namespace bson { +class Bson; +using BsonArray = std::vector; +template +class IndexedMap; +using BsonDocument = IndexedMap; +} // namespace bson + enum class ExpressionComparisonType : unsigned char { Any, All, @@ -570,6 +578,7 @@ class Table { const query_parser::KeyPathMapping& mapping) const; Query query(const std::string& query_string, query_parser::Arguments& arguments, const query_parser::KeyPathMapping&) const; + Query query(const bson::BsonDocument& document) const; //@{ /// WARNING: The link() and backlink() methods will alter a state on the Table object and return a reference diff --git a/src/realm/util/bson/bson.cpp b/src/realm/util/bson/bson.cpp index 7b59673051b..7d5a3195a4a 100644 --- a/src/realm/util/bson/bson.cpp +++ b/src/realm/util/bson/bson.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -797,7 +798,13 @@ Bson dom_obj_to_bson(const Json& json) Bson parse(const std::string_view& json) { - return dom_elem_to_bson(Json::parse(json)); + try { + return dom_elem_to_bson(Json::parse(json)); + } + catch (const std::exception& e) { + throw query_parser::SyntaxError(e.what()); + } + return {}; } } // namespace bson diff --git a/test/object-store/results.cpp b/test/object-store/results.cpp index 10510dc7afe..76739390338 100644 --- a/test/object-store/results.cpp +++ b/test/object-store/results.cpp @@ -5016,6 +5016,145 @@ TEST_CASE("results: public name declared", "[results]") { } } +TEST_CASE("results: bson constructor", "[results]") { + InMemoryTestFile config; + // config.cache = false; + config.automatic_change_notifications = false; + config.schema = Schema{ + {"shared", + ObjectSchema::ObjectType::Embedded, + { + {"weight", PropertyType::Double}, + {"height", PropertyType::Double}, + }}, + {"object", + {{"int_col", PropertyType::Int}, + {"str_col", PropertyType::String}, + {"link_col", PropertyType::Object | PropertyType::Nullable, "shared"}}}, + }; + + auto realm = Realm::get_shared_realm(config); + auto table = realm->read_group().get_table("class_object"); + auto int_col = table->get_column_key("int_col"); + auto str_col = table->get_column_key("str_col"); + auto link_col = table->get_column_key("link_col"); + + realm->begin_transaction(); + for (int i = 0; i < 8; ++i) { + Obj obj = table->create_object(); + obj.set(int_col, (i + 2) % 4); + obj.set(str_col, "hello"); + auto sub = obj.create_and_set_linked_object(link_col); + sub.set("weight", 60. + i); + sub.set("height", 170. + i); + } + realm->commit_transaction(); + Results r(realm, table); + + SECTION("test no operator equals") { + auto object_results = r.find("int_col = 0"); + CHECK(object_results.size() == 2); + object_results = r.find(R""""( + { + "int_col": 0 + } + )""""); + CHECK(object_results.size() == 2); + object_results = r.find(R""""( + { + "int_col": 1 + } + )""""); + CHECK(object_results.size() == 2); + object_results = r.find(R""""( + { + "str_col": "hello" + } + )""""); + CHECK(object_results.size() == 8); + object_results = r.find(R""""( + { + "str_col": "bye" + } + )""""); + CHECK(object_results.size() == 0); + } + SECTION("test int comparison operators") { + auto object_results = r.find(R""""( + { + "int_col": { "$eq": 1 } + } + )""""); + CHECK(object_results.size() == 2); + object_results = r.find(R""""( + { + "int_col": { "$gt": 2 } + } + )""""); + CHECK(object_results.size() == 2); + object_results = r.find(R""""( + { + "int_col": { "$gte": 2 } + } + )""""); + CHECK(object_results.size() == 4); + object_results = r.find(R""""( + { + "int_col": { "$lt": 2 } + } + )""""); + CHECK(object_results.size() == 4); + object_results = r.find(R""""( + { + "int_col": { "$lte": 2 } + } + )""""); + CHECK(object_results.size() == 6); + object_results = r.find(R""""( + { + "link_col.weight": { "$gt": 62 } + } + )""""); + CHECK(object_results.size() == 5); + object_results = r.find(R""""( + { + "int_col": { "$in": [2, 3] } + } + )""""); + CHECK(object_results.size() == 4); + } + SECTION("test logical AND operator") { + auto object_results = r.find(R""""( + { + "$and": [ + { "int_col": { "$gt": 2 } }, + { "str_col": { "$eq": "hello" } } + ] + } + )""""); + CHECK(object_results.size() == 2); + object_results = r.find(R""""( + { + "$and": [ + { "int_col": { "$gt": 2 } }, + { "str_col": { "$eq": "bye" } } + ] + } + )""""); + CHECK(object_results.size() == 0); + } + SECTION("Rainy day") { + CHECK_THROWS_AS(r.find(R"({"non-existing-property": "something"})"), query_parser::InvalidQueryError); + CHECK_THROWS_AS(r.find(R"({"$and": { "int_col": { "$gt": 2 } }})"), query_parser::InvalidQueryError); + CHECK_THROWS_AS(r.find(R"({"$annd": [ + { "int_col": { "$gt": 2 } }, + { "str_col": { "$eq": "hello" } } + ]})"), + query_parser::InvalidQueryError); + CHECK_THROWS_AS(r.find(R"({"int_col": })"), query_parser::SyntaxError); + } +} + TEST_CASE("notifications: objects with PK recreated", "[results]") { _impl::RealmCoordinator::assert_no_open_realms(); InMemoryTestFile config;