diff --git a/src/parser/parser.cpp b/src/parser/parser.cpp index a16000ee..6819a954 100644 --- a/src/parser/parser.cpp +++ b/src/parser/parser.cpp @@ -66,8 +66,9 @@ 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 case_insensitive : pegtl_istring_t("[c]"){}; -struct eq : sor< two< '=' >, one< '=' > > {}; +struct eq : seq< sor< two< '=' >, one< '=' > >, opt< case_insensitive > >{}; struct noteq : pegtl::string< '!', '=' > {}; struct lteq : pegtl::string< '<', '=' > {}; struct lt : one< '<' > {}; @@ -80,7 +81,7 @@ struct ends : pegtl_istring_t("endswith") {}; template struct pad_plus : seq< plus< B >, A, plus< B > > {}; -struct padded_oper : pad_plus< sor< contains, begins, ends >, blank > {}; +struct padded_oper : pad_plus< seq< sor< contains, begins, ends>, opt< case_insensitive > >, blank > {}; struct symbolic_oper : pad< sor< eq, noteq, lteq, lt, gteq, gt >, blank > {}; // predicates @@ -236,8 +237,7 @@ EXPRESSION_ACTION(number, Expression::Type::Number) EXPRESSION_ACTION(true_value, Expression::Type::True) EXPRESSION_ACTION(false_value, Expression::Type::False) EXPRESSION_ACTION(argument_index, Expression::Type::Argument) - - + template<> struct action< true_pred > { static void apply( const input & in, ParserState & state ) @@ -271,7 +271,16 @@ OPERATOR_ACTION(lt, Predicate::Operator::LessThan) OPERATOR_ACTION(begins, Predicate::Operator::BeginsWith) OPERATOR_ACTION(ends, Predicate::Operator::EndsWith) OPERATOR_ACTION(contains, Predicate::Operator::Contains) - + +template<> struct action< case_insensitive > +{ + static void apply( const input & in, ParserState & state ) + { + DEBUG_PRINT_TOKEN(in.string()); + state.last_predicate()->cmpr.option = Predicate::OperatorOption::CaseInsensitive; + } +}; + template<> struct action< one< '(' > > { static void apply( const input & in, ParserState & state ) diff --git a/src/parser/parser.hpp b/src/parser/parser.hpp index 21143538..2f2ebed2 100644 --- a/src/parser/parser.hpp +++ b/src/parser/parser.hpp @@ -57,10 +57,17 @@ struct Predicate EndsWith, Contains }; + + enum class OperatorOption + { + None, + CaseInsensitive, + }; struct Comparison { Operator op = Operator::None; + OperatorOption option = OperatorOption::None; Expression expr[2] = {{Expression::Type::None, ""}, {Expression::Type::None, ""}}; }; diff --git a/src/parser/query_builder.cpp b/src/parser/query_builder.cpp index 8a6ddf54..69eb8e54 100644 --- a/src/parser/query_builder.cpp +++ b/src/parser/query_builder.cpp @@ -164,11 +164,11 @@ void add_bool_constraint_to_query(Query &query, Predicate::Operator operatorType } void add_string_constraint_to_query(Query &query, - Predicate::Operator op, + Predicate::Comparison cmp, Columns &&column, std::string &&value) { - bool case_sensitive = true; - switch (op) { + bool case_sensitive = (cmp.option != Predicate::OperatorOption::CaseInsensitive); + switch (cmp.op) { case Predicate::Operator::BeginsWith: query.and_query(column.begins_with(value, case_sensitive)); break; @@ -190,11 +190,11 @@ void add_string_constraint_to_query(Query &query, } void add_string_constraint_to_query(realm::Query &query, - Predicate::Operator op, + Predicate::Comparison cmp, std::string &&value, Columns &&column) { - bool case_sensitive = true; - switch (op) { + bool case_sensitive = (cmp.option != Predicate::OperatorOption::CaseInsensitive); + switch (cmp.op) { case Predicate::Operator::Equal: query.and_query(column.equal(value, case_sensitive)); break; @@ -396,42 +396,42 @@ auto value_of_type_for_query(TableGetter&& tables, Value&& value, Arguments &arg } template -void do_add_comparison_to_query(Query &query, const Schema &schema, const ObjectSchema &object_schema, Predicate::Operator op, +void do_add_comparison_to_query(Query &query, const Schema &schema, const ObjectSchema &object_schema, Predicate::Comparison cmp, const PropertyExpression &expr, A &lhs, B &rhs, Arguments &args) { auto type = expr.prop->type; switch (type) { case PropertyTypeBool: - add_bool_constraint_to_query(query, op, value_of_type_for_query(expr.table_getter, lhs, args), - value_of_type_for_query(expr.table_getter, rhs, args)); + add_bool_constraint_to_query(query, cmp.op, value_of_type_for_query(expr.table_getter, lhs, args), + value_of_type_for_query(expr.table_getter, rhs, args)); break; case PropertyTypeDate: - add_numeric_constraint_to_query(query, op, value_of_type_for_query(expr.table_getter, lhs, args), - value_of_type_for_query(expr.table_getter, rhs, args)); + add_numeric_constraint_to_query(query, cmp.op, value_of_type_for_query(expr.table_getter, lhs, args), + value_of_type_for_query(expr.table_getter, rhs, args)); break; case PropertyTypeDouble: - add_numeric_constraint_to_query(query, op, value_of_type_for_query(expr.table_getter, lhs, args), - value_of_type_for_query(expr.table_getter, rhs, args)); + add_numeric_constraint_to_query(query, cmp.op, value_of_type_for_query(expr.table_getter, lhs, args), + value_of_type_for_query(expr.table_getter, rhs, args)); break; case PropertyTypeFloat: - add_numeric_constraint_to_query(query, op, value_of_type_for_query(expr.table_getter, lhs, args), - value_of_type_for_query(expr.table_getter, rhs, args)); + add_numeric_constraint_to_query(query, cmp.op, value_of_type_for_query(expr.table_getter, lhs, args), + value_of_type_for_query(expr.table_getter, rhs, args)); break; case PropertyTypeInt: - add_numeric_constraint_to_query(query, op, value_of_type_for_query(expr.table_getter, lhs, args), - value_of_type_for_query(expr.table_getter, rhs, args)); + add_numeric_constraint_to_query(query, cmp.op, value_of_type_for_query(expr.table_getter, lhs, args), + value_of_type_for_query(expr.table_getter, rhs, args)); break; case PropertyTypeString: - add_string_constraint_to_query(query, op, value_of_type_for_query(expr.table_getter, lhs, args), - value_of_type_for_query(expr.table_getter, rhs, args)); + add_string_constraint_to_query(query, cmp, value_of_type_for_query(expr.table_getter, lhs, args), + value_of_type_for_query(expr.table_getter, rhs, args)); break; case PropertyTypeData: - add_binary_constraint_to_query(query, op, value_of_type_for_query(expr.table_getter, lhs, args), - value_of_type_for_query(expr.table_getter, rhs, args)); + add_binary_constraint_to_query(query, cmp.op, value_of_type_for_query(expr.table_getter, lhs, args), + value_of_type_for_query(expr.table_getter, rhs, args)); break; case PropertyTypeObject: case PropertyTypeArray: - add_link_constraint_to_query(query, op, expr, link_argument(lhs, rhs, args)); + add_link_constraint_to_query(query, cmp.op, expr, link_argument(lhs, rhs, args)); break; default: { throw std::runtime_error((std::string)"Object type " + string_for_property_type(type) + " not supported"); @@ -446,11 +446,11 @@ 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.op, expr, expr, cmpr.expr[1], args); + 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.op, expr, cmpr.expr[0], expr, args); + 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"); diff --git a/tests/query.json b/tests/query.json index bc1d4bc3..e4edd4de 100644 --- a/tests/query.json +++ b/tests/query.json @@ -197,10 +197,10 @@ ["QueryThrows", "StringObject", "stringCol == 123"], ["QueryThrows", "StringObject", "stringCol CONTAINS $0", 1], - ["Disabled", "QueryCount", 3, "StringObject", "stringCol ==[c] 'a'"], - ["Disabled", "QueryCount", 5, "StringObject", "stringCol BEGINSWITH[c] 'A'"], - ["Disabled", "QueryCount", 4, "StringObject", "stringCol ENDSWITH[c] 'c'"], - ["Disabled", "QueryCount", 2, "StringObject", "stringCol CONTAINS[c] 'B'"] + ["QueryCount", 3, "StringObject", "stringCol ==[c] 'a'"], + ["QueryCount", 5, "StringObject", "stringCol BEGINSWITH[c] 'A'"], + ["QueryCount", 4, "StringObject", "stringCol ENDSWITH[c] 'c'"], + ["QueryCount", 2, "StringObject", "stringCol CONTAINS[c] 'B'"] ] },