diff --git a/src/object-store/src/parser/parser.cpp b/src/object-store/src/parser/parser.cpp index 6819a954..6b981b3e 100644 --- a/src/object-store/src/parser/parser.cpp +++ b/src/object-store/src/parser/parser.cpp @@ -56,6 +56,7 @@ struct number : seq< minus, sor< float_num, hex_num, int_num > > {}; struct true_value : pegtl_istring_t("true") {}; struct false_value : pegtl_istring_t("false") {}; +struct null_value : pegtl_istring_t("null") {}; // key paths struct key_path : list< seq< sor< alpha, one< '_' > >, star< sor< alnum, one< '_', '-' > > > >, one< '.' > > {}; @@ -65,7 +66,7 @@ struct argument_index : plus< digit > {}; struct argument : seq< one< '$' >, must< argument_index > > {}; // expressions and operators -struct expr : sor< dq_string, sq_string, number, argument, true_value, false_value, key_path > {}; +struct expr : sor< dq_string, sq_string, number, argument, true_value, false_value, null_value, key_path > {}; struct case_insensitive : pegtl_istring_t("[c]"){}; struct eq : seq< sor< two< '=' >, one< '=' > >, opt< case_insensitive > >{}; @@ -236,6 +237,7 @@ EXPRESSION_ACTION(key_path, Expression::Type::KeyPath) EXPRESSION_ACTION(number, Expression::Type::Number) EXPRESSION_ACTION(true_value, Expression::Type::True) EXPRESSION_ACTION(false_value, Expression::Type::False) +EXPRESSION_ACTION(null_value, Expression::Type::Null) EXPRESSION_ACTION(argument_index, Expression::Type::Argument) template<> struct action< true_pred > diff --git a/src/object-store/src/parser/parser.hpp b/src/object-store/src/parser/parser.hpp index 2f2ebed2..1e8afef8 100644 --- a/src/object-store/src/parser/parser.hpp +++ b/src/object-store/src/parser/parser.hpp @@ -28,7 +28,7 @@ class Schema; namespace parser { struct Expression { - enum class Type { None, Number, String, KeyPath, Argument, True, False } type; + enum class Type { None, Number, String, KeyPath, Argument, True, False, Null } type; std::string s; Expression(Type t = Type::None, std::string s = "") : type(t), s(s) {} }; diff --git a/src/object-store/src/parser/query_builder.cpp b/src/object-store/src/parser/query_builder.cpp index 69eb8e54..a93f7863 100644 --- a/src/object-store/src/parser/query_builder.cpp +++ b/src/object-store/src/parser/query_builder.cpp @@ -265,22 +265,6 @@ void add_link_constraint_to_query(realm::Query &query, } } -void add_link_constraint_to_query(realm::Query &query, - Predicate::Operator op, - PropertyExpression &prop_expr, - realm::null) { - precondition(prop_expr.indexes.empty(), "KeyPath queries not supported for object comparisons."); - switch (op) { - case Predicate::Operator::NotEqual: - query.Not(); - case Predicate::Operator::Equal: - query.and_query(query.get_table()->column(prop_expr.prop->table_column).is_null()); - break; - default: - throw std::runtime_error("Only 'equal' and 'not equal' operators supported for object comparison."); - } -} - auto link_argument(const PropertyExpression &propExpr, const parser::Expression &argExpr, Arguments &args) { return args.object_index_for_argument(stot(argExpr.s)); @@ -438,6 +422,50 @@ void do_add_comparison_to_query(Query &query, const Schema &schema, const Object } } } + +void do_add_null_comparison_to_query(Query &query, const Schema &schema, const ObjectSchema &object_schema, Predicate::Comparison cmp, const PropertyExpression &expr, Arguments &args) +{ + auto type = expr.prop->type; + switch (type) { + case PropertyTypeObject: + precondition(expr.indexes.empty(), "KeyPath queries not supported for object comparisons."); + switch (cmp.op) { + case Predicate::Operator::NotEqual: + query.Not(); + case Predicate::Operator::Equal: + query.and_query(query.get_table()->column(expr.prop->table_column).is_null()); + break; + default: + throw std::runtime_error("Only 'equal' and 'not equal' operators supported for object comparison."); + } + break; + case PropertyTypeArray: + throw std::runtime_error((std::string)"Comparing Lists to 'null' is not supported"); + break; + default: { + switch (cmp.op) { + case Predicate::Operator::NotEqual: + query.not_equal(expr.prop->table_column, realm::null()); + break; + case Predicate::Operator::Equal: + query.equal(expr.prop->table_column, realm::null()); + break; + default: + throw std::runtime_error("Only 'equal' and 'not equal' operators supported for object comparison."); + } + } + } +} + +bool expression_is_null(const parser::Expression &expr, Arguments &args) { + if (expr.type == parser::Expression::Type::Null) { + return true; + } + else if (expr.type == parser::Expression::Type::Argument) { + return args.is_argument_null(stot(expr.s)); + } + return false; +} void add_comparison_to_query(Query &query, const Predicate &pred, Arguments &args, const Schema &schema, const std::string &type) { @@ -446,11 +474,21 @@ void add_comparison_to_query(Query &query, const Predicate &pred, Arguments &arg auto object_schema = schema.find(type); if (t0 == parser::Expression::Type::KeyPath && t1 != parser::Expression::Type::KeyPath) { PropertyExpression expr(query, schema, object_schema, cmpr.expr[0].s); - do_add_comparison_to_query(query, schema, *object_schema, cmpr, expr, expr, cmpr.expr[1], args); + if (expression_is_null(cmpr.expr[1], args)) { + do_add_null_comparison_to_query(query, schema, *object_schema, cmpr, expr, args); + } + else { + do_add_comparison_to_query(query, schema, *object_schema, cmpr, expr, expr, cmpr.expr[1], args); + } } else if (t0 != parser::Expression::Type::KeyPath && t1 == parser::Expression::Type::KeyPath) { PropertyExpression expr(query, schema, object_schema, cmpr.expr[1].s); - do_add_comparison_to_query(query, schema, *object_schema, cmpr, expr, cmpr.expr[0], expr, args); + if (expression_is_null(cmpr.expr[0], args)) { + do_add_null_comparison_to_query(query, schema, *object_schema, cmpr, expr, args); + } + else { + do_add_comparison_to_query(query, schema, *object_schema, cmpr, expr, cmpr.expr[0], expr, args); + } } else { throw std::runtime_error("Predicate expressions must compare a keypath and another keypath or a constant value");