From 812930285e957cff81480968080a0e306007db84 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Tue, 24 Nov 2015 10:39:42 -0800 Subject: [PATCH 1/8] compount parser tests --- src/object-store/parser/queryTests.json | 24 +++++++++++++ tests/QueryTests.js | 48 +++++++++++++++++-------- 2 files changed, 58 insertions(+), 14 deletions(-) diff --git a/src/object-store/parser/queryTests.json b/src/object-store/parser/queryTests.json index f9a5f56f..75daa28f 100644 --- a/src/object-store/parser/queryTests.json +++ b/src/object-store/parser/queryTests.json @@ -245,6 +245,30 @@ ["QueryThrows", "LinkObject", "linkCol > $0", [0, "linkCol"]], ["QueryThrows", "LinkObject", "intCol = $0", [0, "linkCol"]] ] +}, + +"compoundTests" : { + "schema" : [ + { "name": "IntObject", + "properties": [{ "name": "intCol", "type": "int" }], + "primaryKey" : "intCol" } + ], + "objects": [ + { "type": "IntObject", "value": [0] }, + { "type": "IntObject", "value": [1] }, + { "type": "IntObject", "value": [2] }, + { "type": "IntObject", "value": [3] } + ], + "tests": [ + ["ObjectSet", [], "IntObject", "intCol == 0 && intCol == 1"], + ["ObjectSet", [0, 1], "IntObject", "intCol == 0 || intCol == 1"], + ["ObjectSet", [0], "IntObject", "intCol == 0 && intCol != 1"], + ["ObjectSet", [2, 3], "IntObject", "intCol >= 2 && intCol < 4"], + ["ObjectSet", [0], "IntObject", "intCol == 0 && NOT intCol != 0"], + ["ObjectSet", [1], "IntObject", "(intCol == 0 || intCol == 1) && intCol >= 1"], + ["ObjectSet", [0, 1], "IntObject", "intCol == 0 || (intCol == 1 && intCol >= 1)"], + ["ObjectSet", [0, 1], "IntObject", "intCol == 0 || intCol == 1 && intCol >= 1"] + ] } } diff --git a/tests/QueryTests.js b/tests/QueryTests.js index e49cb388..a26c6c02 100644 --- a/tests/QueryTests.js +++ b/tests/QueryTests.js @@ -37,27 +37,44 @@ function runQuerySuite(suite) { } }); + var args; + function getArgs(startArg) { + args = test.slice(startArg, startArg + 2); + for (var i = startArg + 2; i < test.length; i++) { + var arg = test[i]; + if (Array.isArray(arg)) { + // aray arguments correspond to [objectAtIndex, propertyName] + args.push(objects[arg[0]][arg[1]]); + } + else { + args.push(arg); + } + } + return args; + } + for (var test of suite.tests) { if (test[0] == "QueryCount") { - var args = test.slice(2, 4); - for (var i = 4; i < test.length; i++) { - var arg = test[i]; - if (Array.isArray(arg)) { - // aray arguments correspond to [objectAtIndex, propertyName] - args.push(objects[arg[0]][arg[1]]); - } - else { - args.push(arg); - } + var length = realm.objects.apply(realm, getArgs(2)).length; + TestCase.assertEqual(test[1], length, "Query '" + args[1] + "' on type '" + args[0] + "' expected " + test[1] + " results, got " + length); + } + else if (test[0] == "ObjectSet") { + var results = realm.objects.apply(realm, getArgs(2)); + TestCase.assertEqual(test[1].length, results.length, "Query '" + args[1] + "' on type '" + args[0] + "' expected " + test[1].length + " results, got " + results.length); + + var objSchema = suite.schema.find(function(el) { return el.name == args[0] }); + var primary = objSchema.primaryKey; + if (!primary) { + throw "Primary key required for object comparison"; } - var results = realm.objects.apply(realm, args); - TestCase.assertEqual(test[1], results.length, "Query '" + args[1] + "' on type '" + args[0] + "' expected " + test[1] + " results, got " + results.length); + TestCase.assertArraysEqual(test[1], Array.prototype.map.call(results, function(el) { + return el[primary] + })); } else if (test[0] == "QueryThrows") { - var args = test.slice(1); TestCase.assertThrows(function() { - realm.objects.apply(realm, args); + realm.objects.apply(realm, getArgs(1)); }, "Expected exception not thrown for query: " + JSON.stringify(args)); } else if (test[0] != "Disabled") { @@ -91,6 +108,9 @@ module.exports = BaseTest.extend({ }, testObjectQueries: function() { runQuerySuite(testCases.objectTests); + }, + testCompoundQueries: function() { + runQuerySuite(testCases.compoundTests); } }); From 2324f8cc9b251a26a392c2fe18baaf0d8f269407 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Tue, 24 Nov 2015 11:18:03 -0800 Subject: [PATCH 2/8] fix for mixed && and || queries --- src/object-store/parser/parser.cpp | 34 +++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/src/object-store/parser/parser.cpp b/src/object-store/parser/parser.cpp index 98c81ac5..9765eeb7 100644 --- a/src/object-store/parser/parser.cpp +++ b/src/object-store/parser/parser.cpp @@ -107,27 +107,37 @@ struct pred : seq< and_pred, star< or_ext > > {}; struct ParserState { std::vector predicate_stack; - Predicate ¤t() { + Predicate ¤t() + { return *predicate_stack.back(); } - + + bool current_is_compound() + { + return current().type == Predicate::Type::And || current().type == Predicate::Type::Or; + } + bool negate_next = false; - + void addExpression(Expression && exp) { - if (current().type == Predicate::Type::Comparison) { - current().cmpr.expr[1] = std::move(exp); + Predicate &cur = current(); + if (cur.type == Predicate::Type::Comparison) { + cur.cmpr.expr[1] = std::move(exp); predicate_stack.pop_back(); } else { + assert(current_is_compound()); + Predicate p(Predicate::Type::Comparison); p.cmpr.expr[0] = std::move(exp); if (negate_next) { p.negate = true; negate_next = false; } - current().cpnd.sub_predicates.emplace_back(std::move(p)); - predicate_stack.push_back(¤t().cpnd.sub_predicates.back()); + + cur.cpnd.sub_predicates.emplace_back(std::move(p)); + predicate_stack.push_back(&cur.cpnd.sub_predicates.back()); } } }; @@ -147,6 +157,7 @@ template<> struct action< and_ext > static void apply( const input & in, ParserState & state ) { DEBUG_PRINT_TOKEN(""); + assert(state.current_is_compound()); // if we were put into an OR group we need to rearrange auto ¤t = state.current(); @@ -177,6 +188,7 @@ template<> struct action< or_ext > static void apply( const input & in, ParserState & state ) { DEBUG_PRINT_TOKEN(""); + assert(state.current_is_compound()); // if already an OR group do nothing auto ¤t = state.current(); @@ -186,15 +198,17 @@ template<> struct action< or_ext > // if only two predicates in the group, then convert to OR auto &sub_preds = state.current().cpnd.sub_predicates; - if (sub_preds.size()) { + assert(sub_preds.size() > 1); + if (sub_preds.size() == 2) { current.type = Predicate::Type::Or; return; } // split the current group into to groups which are ORed together Predicate pred1(Predicate::Type::And), pred2(Predicate::Type::And); - pred1.cpnd.sub_predicates.insert(sub_preds.begin(), sub_preds.back()); - pred2.cpnd.sub_predicates.push_back(std::move(sub_preds.back())); + std::vector::iterator second_last = sub_preds.end() - 2; + pred1.cpnd.sub_predicates.insert(pred1.cpnd.sub_predicates.begin(), sub_preds.begin(), second_last); + pred2.cpnd.sub_predicates.insert(pred2.cpnd.sub_predicates.begin(), second_last, sub_preds.end()); current.type = Predicate::Type::Or; sub_preds.clear(); From d967a8a4e4ae6802222d6fd62464cd1d293fbbe6 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Wed, 25 Nov 2015 12:49:31 -0800 Subject: [PATCH 3/8] process compound operators in the correct order --- src/object-store/parser/parser.cpp | 173 ++++++++++++------------ src/object-store/parser/parser.hpp | 4 +- src/object-store/parser/queryTests.json | 3 +- 3 files changed, 91 insertions(+), 89 deletions(-) diff --git a/src/object-store/parser/parser.cpp b/src/object-store/parser/parser.cpp index 9765eeb7..b9b0373d 100644 --- a/src/object-store/parser/parser.cpp +++ b/src/object-store/parser/parser.cpp @@ -106,38 +106,91 @@ struct pred : seq< and_pred, star< or_ext > > {}; // state struct ParserState { - std::vector predicate_stack; - Predicate ¤t() + std::vector group_stack; + + Predicate *current_group() { - return *predicate_stack.back(); + return group_stack.back(); } - bool current_is_compound() + Predicate *last_predicate() { - return current().type == Predicate::Type::And || current().type == Predicate::Type::Or; + Predicate *pred = current_group(); + while (pred->type != Predicate::Type::Comparison && pred->cpnd.sub_predicates.size()) { + pred = &pred->cpnd.sub_predicates.back(); + } + return pred; + } + + void add_predicate_to_current_group(Predicate::Type type) + { + current_group()->cpnd.sub_predicates.emplace_back(type, negate_next); + negate_next = false; + + if (current_group()->cpnd.sub_predicates.size() > 1) { + if (next_type == Predicate::Type::Or) { + apply_or(); + } + else { + apply_and(); + } + } } bool negate_next = false; + Predicate::Type next_type = Predicate::Type::And; - void addExpression(Expression && exp) + void add_expression(Expression && exp) { - Predicate &cur = current(); - if (cur.type == Predicate::Type::Comparison) { - cur.cmpr.expr[1] = std::move(exp); - predicate_stack.pop_back(); + Predicate *current = last_predicate(); + if (current->type == Predicate::Type::Comparison && current->cmpr.expr[1].type == parser::Expression::Type::None) { + current->cmpr.expr[1] = std::move(exp); } else { - assert(current_is_compound()); - - Predicate p(Predicate::Type::Comparison); - p.cmpr.expr[0] = std::move(exp); - if (negate_next) { - p.negate = true; - negate_next = false; - } + add_predicate_to_current_group(Predicate::Type::Comparison); + last_predicate()->cmpr.expr[0] = std::move(exp); + } + } + + void apply_or() + { + Predicate &group = *group_stack.back(); + if (group.type == Predicate::Type::Or) { + return; + } + + // convert to OR + group.type = Predicate::Type::Or; + if (group.cpnd.sub_predicates.size() > 2) { + // split the current group into an AND group ORed with the last subpredicate + Predicate new_sub(Predicate::Type::And); + new_sub.cpnd.sub_predicates = std::move(group.cpnd.sub_predicates); - cur.cpnd.sub_predicates.emplace_back(std::move(p)); - predicate_stack.push_back(&cur.cpnd.sub_predicates.back()); + group.cpnd.sub_predicates = { new_sub, std::move(new_sub.cpnd.sub_predicates.back()) }; + group.cpnd.sub_predicates[0].cpnd.sub_predicates.pop_back(); + } + } + + void apply_and() + { + if (current_group()->type == Predicate::Type::And) { + return; + } + + auto &sub_preds = current_group()->cpnd.sub_predicates; + auto second_last = sub_preds.end() - 2; + if (second_last->type == Predicate::Type::And) { + // if we are in an OR group and second to last predicate group is + // an AND group then move the last predicate inside + second_last->cpnd.sub_predicates.push_back(std::move(sub_preds.back())); + sub_preds.pop_back(); + } + else { + // otherwise combine last two into a new AND group + Predicate pred(Predicate::Type::And); + pred.cpnd.sub_predicates.insert(pred.cpnd.sub_predicates.begin(), second_last, sub_preds.end()); + sub_preds.erase(second_last, sub_preds.end()); + sub_preds.emplace_back(std::move(pred)); } } }; @@ -152,68 +205,21 @@ struct action : nothing< Rule > {}; #define DEBUG_PRINT_TOKEN(string) #endif -template<> struct action< and_ext > +template<> struct action< and_op > { static void apply( const input & in, ParserState & state ) { DEBUG_PRINT_TOKEN(""); - assert(state.current_is_compound()); - - // if we were put into an OR group we need to rearrange - auto ¤t = state.current(); - if (current.type == Predicate::Type::Or) { - auto &sub_preds = current.cpnd.sub_predicates; - auto &second_last = sub_preds[sub_preds.size()-2]; - if (second_last.type == Predicate::Type::And) { - // if we are in an OR group and second to last predicate group is - // an AND group then move the last predicate inside - second_last.cpnd.sub_predicates.push_back(std::move(sub_preds.back())); - sub_preds.pop_back(); - } - else { - // otherwise combine last two into a new AND group - Predicate pred(Predicate::Type::And); - pred.cpnd.sub_predicates.emplace_back(std::move(second_last)); - pred.cpnd.sub_predicates.emplace_back(std::move(sub_preds.back())); - sub_preds.pop_back(); - sub_preds.pop_back(); - sub_preds.push_back(std::move(pred)); - } - } + state.next_type = Predicate::Type::And; } }; -template<> struct action< or_ext > +template<> struct action< or_op > { static void apply( const input & in, ParserState & state ) { DEBUG_PRINT_TOKEN(""); - assert(state.current_is_compound()); - - // if already an OR group do nothing - auto ¤t = state.current(); - if (current.type == Predicate::Type::Or) { - return; - } - - // if only two predicates in the group, then convert to OR - auto &sub_preds = state.current().cpnd.sub_predicates; - assert(sub_preds.size() > 1); - if (sub_preds.size() == 2) { - current.type = Predicate::Type::Or; - return; - } - - // split the current group into to groups which are ORed together - Predicate pred1(Predicate::Type::And), pred2(Predicate::Type::And); - std::vector::iterator second_last = sub_preds.end() - 2; - pred1.cpnd.sub_predicates.insert(pred1.cpnd.sub_predicates.begin(), sub_preds.begin(), second_last); - pred2.cpnd.sub_predicates.insert(pred2.cpnd.sub_predicates.begin(), second_last, sub_preds.end()); - - current.type = Predicate::Type::Or; - sub_preds.clear(); - sub_preds.emplace_back(std::move(pred1)); - sub_preds.emplace_back(std::move(pred2)); + state.next_type = Predicate::Type::Or; } }; @@ -222,7 +228,7 @@ template<> struct action< or_ext > template<> struct action< rule > { \ static void apply( const input & in, ParserState & state ) { \ DEBUG_PRINT_TOKEN(in.string()); \ - state.addExpression(Expression(type, in.string())); }}; + state.add_expression(Expression(type, in.string())); }}; EXPRESSION_ACTION(dq_string_content, Expression::Type::String) EXPRESSION_ACTION(sq_string_content, Expression::Type::String) @@ -238,7 +244,7 @@ template<> struct action< true_pred > static void apply( const input & in, ParserState & state ) { DEBUG_PRINT_TOKEN(in.string()); - state.current().cpnd.sub_predicates.emplace_back(Predicate::Type::True); + state.current_group()->cpnd.sub_predicates.emplace_back(Predicate::Type::True); } }; @@ -247,7 +253,7 @@ template<> struct action< false_pred > static void apply( const input & in, ParserState & state ) { DEBUG_PRINT_TOKEN(in.string()); - state.current().cpnd.sub_predicates.emplace_back(Predicate::Type::False); + state.current_group()->cpnd.sub_predicates.emplace_back(Predicate::Type::False); } }; @@ -255,7 +261,7 @@ template<> struct action< false_pred > template<> struct action< rule > { \ static void apply( const input & in, ParserState & state ) { \ DEBUG_PRINT_TOKEN(in.string()); \ - state.current().cmpr.op = oper; }}; + state.last_predicate()->cmpr.op = oper; }}; OPERATOR_ACTION(eq, Predicate::Operator::Equal) OPERATOR_ACTION(noteq, Predicate::Operator::NotEqual) @@ -272,15 +278,8 @@ template<> struct action< one< '(' > > static void apply( const input & in, ParserState & state ) { DEBUG_PRINT_TOKEN(""); - - Predicate group(Predicate::Type::And); - if (state.negate_next) { - group.negate = true; - state.negate_next = false; - } - - state.current().cpnd.sub_predicates.emplace_back(std::move(group)); - state.predicate_stack.push_back(&state.current().cpnd.sub_predicates.back()); + state.add_predicate_to_current_group(Predicate::Type::And); + state.group_stack.push_back(state.last_predicate()); } }; @@ -289,7 +288,7 @@ template<> struct action< group_pred > static void apply( const input & in, ParserState & state ) { DEBUG_PRINT_TOKEN(""); - state.predicate_stack.pop_back(); + state.group_stack.pop_back(); } }; @@ -322,10 +321,12 @@ const std::string error_message_control< Rule >::error_message = "Invalid predic Predicate parse(const std::string &query) { + DEBUG_PRINT_TOKEN(query); + Predicate out_predicate(Predicate::Type::And); ParserState state; - state.predicate_stack.push_back(&out_predicate); + state.group_stack.push_back(&out_predicate); pegtl::parse< must< pred, eof >, action, error_message_control >(query, query, state); if (out_predicate.type == Predicate::Type::And && out_predicate.cpnd.sub_predicates.size() == 1) { diff --git a/src/object-store/parser/parser.hpp b/src/object-store/parser/parser.hpp index f443c5f1..4081f3d3 100644 --- a/src/object-store/parser/parser.hpp +++ b/src/object-store/parser/parser.hpp @@ -28,7 +28,7 @@ namespace realm { namespace parser { struct Expression { - enum class Type { Number, String, KeyPath, Argument, True, False } type; + enum class Type { Number, String, KeyPath, Argument, True, False, None } type = Type::None; std::string s; Expression() {} Expression(Type t, std::string s) : type(t), s(s) {} @@ -76,7 +76,7 @@ namespace realm { bool negate = false; - Predicate(Type t) : type(t) {} + Predicate(Type t, bool n = false) : type(t), negate(n) {} }; Predicate parse(const std::string &query); diff --git a/src/object-store/parser/queryTests.json b/src/object-store/parser/queryTests.json index 75daa28f..518759cc 100644 --- a/src/object-store/parser/queryTests.json +++ b/src/object-store/parser/queryTests.json @@ -267,7 +267,8 @@ ["ObjectSet", [0], "IntObject", "intCol == 0 && NOT intCol != 0"], ["ObjectSet", [1], "IntObject", "(intCol == 0 || intCol == 1) && intCol >= 1"], ["ObjectSet", [0, 1], "IntObject", "intCol == 0 || (intCol == 1 && intCol >= 1)"], - ["ObjectSet", [0, 1], "IntObject", "intCol == 0 || intCol == 1 && intCol >= 1"] + ["ObjectSet", [0, 1], "IntObject", "intCol == 0 || intCol == 1 && intCol >= 1"], + ["ObjectSet", [0, 1], "IntObject", "intCol == 1 && intCol >= 1 || intCol == 0"] ] } From 3fba2f3a195362b88c1bf0944d21d75a58251262 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Wed, 25 Nov 2015 12:54:13 -0800 Subject: [PATCH 4/8] add a few more tests --- src/object-store/parser/queryTests.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/object-store/parser/queryTests.json b/src/object-store/parser/queryTests.json index 518759cc..fb280f19 100644 --- a/src/object-store/parser/queryTests.json +++ b/src/object-store/parser/queryTests.json @@ -266,9 +266,12 @@ ["ObjectSet", [2, 3], "IntObject", "intCol >= 2 && intCol < 4"], ["ObjectSet", [0], "IntObject", "intCol == 0 && NOT intCol != 0"], ["ObjectSet", [1], "IntObject", "(intCol == 0 || intCol == 1) && intCol >= 1"], + ["ObjectSet", [1], "IntObject", "intCol >= 1 && (intCol == 0 || intCol == 1)"], ["ObjectSet", [0, 1], "IntObject", "intCol == 0 || (intCol == 1 && intCol >= 1)"], + ["ObjectSet", [0, 1], "IntObject", "(intCol == 1 && intCol >= 1) || intCol == 0"], ["ObjectSet", [0, 1], "IntObject", "intCol == 0 || intCol == 1 && intCol >= 1"], - ["ObjectSet", [0, 1], "IntObject", "intCol == 1 && intCol >= 1 || intCol == 0"] + ["ObjectSet", [0, 1], "IntObject", "intCol == 1 && intCol >= 1 || intCol == 0"], + ["ObjectSet", [0, 1], "IntObject", "intCol == 1 || intCol == 0 && intCol <= 0 && intCol >= 0"] ] } From 6449d34735c1e5350c259afe4a37beaebf235606 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Wed, 25 Nov 2015 12:57:56 -0800 Subject: [PATCH 5/8] more tests --- src/object-store/parser/queryTests.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/object-store/parser/queryTests.json b/src/object-store/parser/queryTests.json index fb280f19..15f7fefa 100644 --- a/src/object-store/parser/queryTests.json +++ b/src/object-store/parser/queryTests.json @@ -265,11 +265,13 @@ ["ObjectSet", [0], "IntObject", "intCol == 0 && intCol != 1"], ["ObjectSet", [2, 3], "IntObject", "intCol >= 2 && intCol < 4"], ["ObjectSet", [0], "IntObject", "intCol == 0 && NOT intCol != 0"], + ["ObjectSet", [0, 3], "IntObject", "intCol == 0 || NOT (intCol == 1 || intCol == 2)"], ["ObjectSet", [1], "IntObject", "(intCol == 0 || intCol == 1) && intCol >= 1"], ["ObjectSet", [1], "IntObject", "intCol >= 1 && (intCol == 0 || intCol == 1)"], ["ObjectSet", [0, 1], "IntObject", "intCol == 0 || (intCol == 1 && intCol >= 1)"], ["ObjectSet", [0, 1], "IntObject", "(intCol == 1 && intCol >= 1) || intCol == 0"], ["ObjectSet", [0, 1], "IntObject", "intCol == 0 || intCol == 1 && intCol >= 1"], + ["ObjectSet", [0, 1, 2],"IntObject", "intCol == 0 || intCol == 1 || intCol <= 2"], ["ObjectSet", [0, 1], "IntObject", "intCol == 1 && intCol >= 1 || intCol == 0"], ["ObjectSet", [0, 1], "IntObject", "intCol == 1 || intCol == 0 && intCol <= 0 && intCol >= 0"] ] From fbb1f42c6e4e1153204f403fe1439bd213d65362 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Wed, 25 Nov 2015 18:32:13 -0800 Subject: [PATCH 6/8] don't merge predicate to negated and group --- src/object-store/parser/parser.cpp | 5 ++--- src/object-store/parser/queryTests.json | 3 ++- tests/QueryTests.js | 24 ------------------------ 3 files changed, 4 insertions(+), 28 deletions(-) diff --git a/src/object-store/parser/parser.cpp b/src/object-store/parser/parser.cpp index b9b0373d..f478d1c5 100644 --- a/src/object-store/parser/parser.cpp +++ b/src/object-store/parser/parser.cpp @@ -179,9 +179,8 @@ struct ParserState auto &sub_preds = current_group()->cpnd.sub_predicates; auto second_last = sub_preds.end() - 2; - if (second_last->type == Predicate::Type::And) { - // if we are in an OR group and second to last predicate group is - // an AND group then move the last predicate inside + if (second_last->type == Predicate::Type::And && !second_last->negate) { + // make a new and group populated with the last two predicates second_last->cpnd.sub_predicates.push_back(std::move(sub_preds.back())); sub_preds.pop_back(); } diff --git a/src/object-store/parser/queryTests.json b/src/object-store/parser/queryTests.json index 15f7fefa..630694e8 100644 --- a/src/object-store/parser/queryTests.json +++ b/src/object-store/parser/queryTests.json @@ -273,7 +273,8 @@ ["ObjectSet", [0, 1], "IntObject", "intCol == 0 || intCol == 1 && intCol >= 1"], ["ObjectSet", [0, 1, 2],"IntObject", "intCol == 0 || intCol == 1 || intCol <= 2"], ["ObjectSet", [0, 1], "IntObject", "intCol == 1 && intCol >= 1 || intCol == 0"], - ["ObjectSet", [0, 1], "IntObject", "intCol == 1 || intCol == 0 && intCol <= 0 && intCol >= 0"] + ["ObjectSet", [0, 1], "IntObject", "intCol == 1 || intCol == 0 && intCol <= 0 && intCol >= 0"], + ["ObjectSet", [0, 1], "IntObject", "intCol == 0 || NOT (intCol == 3 && intCol >= 0) && intCol == 1"] ] } diff --git a/tests/QueryTests.js b/tests/QueryTests.js index a26c6c02..093716d1 100644 --- a/tests/QueryTests.js +++ b/tests/QueryTests.js @@ -942,30 +942,6 @@ module.exports = BaseTest.extend({ XCTAssertThrows([CircleArrayObject objectsInRealm:realm where:@"NONE data.circles = '2'"]); } -- (void)testCompoundOrQuery { - RLMRealm *realm = [RLMRealm defaultRealm]; - - [realm beginWriteTransaction]; - [PersonObject createInRealm:realm withValue:@[@"Tim", @29]]; - [PersonObject createInRealm:realm withValue:@[@"Ari", @33]]; - [realm commitWriteTransaction]; - - XCTAssertEqual(2U, [[PersonObject objectsWhere:@"name == 'Ari' or age < 30"] count]); - XCTAssertEqual(1U, [[PersonObject objectsWhere:@"name == 'Ari' or age > 40"] count]); -} - -- (void)testCompoundAndQuery { - RLMRealm *realm = [RLMRealm defaultRealm]; - - [realm beginWriteTransaction]; - [PersonObject createInRealm:realm withValue:@[@"Tim", @29]]; - [PersonObject createInRealm:realm withValue:@[@"Ari", @33]]; - [realm commitWriteTransaction]; - - XCTAssertEqual(1U, [[PersonObject objectsWhere:@"name == 'Ari' and age > 30"] count]); - XCTAssertEqual(0U, [[PersonObject objectsWhere:@"name == 'Ari' and age > 40"] count]); -} - - (void)testClass:(Class)class withNormalCount:(NSUInteger)normalCount notCount:(NSUInteger)notCount From 96994a24ccae071ef6400301801bc95f88642bca Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Wed, 25 Nov 2015 18:34:36 -0800 Subject: [PATCH 7/8] use current_group() helper --- src/object-store/parser/parser.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/object-store/parser/parser.cpp b/src/object-store/parser/parser.cpp index f478d1c5..2753fbd8 100644 --- a/src/object-store/parser/parser.cpp +++ b/src/object-store/parser/parser.cpp @@ -154,20 +154,20 @@ struct ParserState void apply_or() { - Predicate &group = *group_stack.back(); - if (group.type == Predicate::Type::Or) { + Predicate *group = current_group(); + if (group->type == Predicate::Type::Or) { return; } // convert to OR - group.type = Predicate::Type::Or; - if (group.cpnd.sub_predicates.size() > 2) { + group->type = Predicate::Type::Or; + if (group->cpnd.sub_predicates.size() > 2) { // split the current group into an AND group ORed with the last subpredicate Predicate new_sub(Predicate::Type::And); - new_sub.cpnd.sub_predicates = std::move(group.cpnd.sub_predicates); + new_sub.cpnd.sub_predicates = std::move(group->cpnd.sub_predicates); - group.cpnd.sub_predicates = { new_sub, std::move(new_sub.cpnd.sub_predicates.back()) }; - group.cpnd.sub_predicates[0].cpnd.sub_predicates.pop_back(); + group->cpnd.sub_predicates = { new_sub, std::move(new_sub.cpnd.sub_predicates.back()) }; + group->cpnd.sub_predicates[0].cpnd.sub_predicates.pop_back(); } } From 1b37e0945f775d884dbed41bde1f24e1eb34f286 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Wed, 25 Nov 2015 19:10:59 -0800 Subject: [PATCH 8/8] make None the first enum type --- src/object-store/parser/parser.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/object-store/parser/parser.hpp b/src/object-store/parser/parser.hpp index 4081f3d3..377eda5d 100644 --- a/src/object-store/parser/parser.hpp +++ b/src/object-store/parser/parser.hpp @@ -28,7 +28,7 @@ namespace realm { namespace parser { struct Expression { - enum class Type { Number, String, KeyPath, Argument, True, False, None } type = Type::None; + enum class Type { None, Number, String, KeyPath, Argument, True, False } type = Type::None; std::string s; Expression() {} Expression(Type t, std::string s) : type(t), s(s) {}