From 031fd00024c05cac3568f5ed027e80991dba18b0 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Wed, 18 Nov 2015 17:55:30 -0800 Subject: [PATCH 01/66] fix for rpc constants and other pr feedback --- list.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/list.hpp b/list.hpp index 54bbed41..507c968e 100644 --- a/list.hpp +++ b/list.hpp @@ -57,6 +57,4 @@ namespace realm { }; } - - #endif /* REALM_LIST_HPP */ From ee6c6eb30fdfc1024b08738aedf5dfc831aab1b8 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Thu, 19 Nov 2015 07:07:33 -0800 Subject: [PATCH 02/66] size_t, no std::size_t --- list.cpp | 12 ++++++------ list.hpp | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/list.cpp b/list.cpp index 1cbf1477..1cbafb8d 100644 --- a/list.cpp +++ b/list.cpp @@ -26,40 +26,40 @@ size_t List::size() { return m_link_view->size(); } -Row List::get(std::size_t row_ndx) { +Row List::get(size_t row_ndx) { verify_attached(); verify_valid_row(row_ndx); return m_link_view->get(row_ndx); } -void List::set(std::size_t row_ndx, std::size_t target_row_ndx) { +void List::set(size_t row_ndx, size_t target_row_ndx) { verify_attached(); verify_in_tranaction(); verify_valid_row(row_ndx); m_link_view->set(row_ndx, target_row_ndx); } -void List::add(std::size_t target_row_ndx) { +void List::add(size_t target_row_ndx) { verify_attached(); verify_in_tranaction(); m_link_view->add(target_row_ndx); } -void List::insert(std::size_t row_ndx, std::size_t target_row_ndx) { +void List::insert(size_t row_ndx, size_t target_row_ndx) { verify_attached(); verify_in_tranaction(); verify_valid_row(row_ndx, true); m_link_view->insert(row_ndx, target_row_ndx); } -void List::remove(std::size_t row_ndx) { +void List::remove(size_t row_ndx) { verify_attached(); verify_in_tranaction(); verify_valid_row(row_ndx); m_link_view->remove(row_ndx); } -void List::verify_valid_row(std::size_t row_ndx, bool insertion) { +void List::verify_valid_row(size_t row_ndx, bool insertion) { size_t size = m_link_view->size(); if (row_ndx > size || (!insertion && row_ndx == size)) { throw std::out_of_range(std::string("Index ") + std::to_string(row_ndx) + " is outside of range 0..." + std::to_string(size) + "."); diff --git a/list.hpp b/list.hpp index 507c968e..fdde4c62 100644 --- a/list.hpp +++ b/list.hpp @@ -31,8 +31,8 @@ namespace realm { SharedRealm realm() { return m_realm; } size_t size(); - Row get(std::size_t row_ndx); - void set(std::size_t row_ndx, std::size_t target_row_ndx); + Row get(size_t row_ndx); + void set(size_t row_ndx, size_t target_row_ndx); void add(size_t target_row_ndx); void remove(size_t list_ndx); @@ -47,7 +47,7 @@ namespace realm { template void set(ContextType ctx, ValueType value, size_t list_ndx); - void verify_valid_row(std::size_t row_ndx, bool insertion = false); + void verify_valid_row(size_t row_ndx, bool insertion = false); void verify_attached(); void verify_in_tranaction(); From b515b4b6d9d0cbb3d290ff26273595d8b480b0eb Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Thu, 5 Nov 2015 16:21:19 -0800 Subject: [PATCH 03/66] beginnings of a parser --- parser/parser.cpp | 65 +++++++++++++++++++++++++++++++++++++++++++++++ parser/query.abnf | 23 +++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 parser/parser.cpp create mode 100644 parser/query.abnf diff --git a/parser/parser.cpp b/parser/parser.cpp new file mode 100644 index 00000000..5d89918e --- /dev/null +++ b/parser/parser.cpp @@ -0,0 +1,65 @@ +#include +#include + +#include + +using namespace pegtl; + +namespace query +{ + // strings + struct unicode : list< seq< one< 'u' >, rep< 4, must< xdigit > > >, one< '\\' > > {}; + struct escaped_char : one< '"', '\\', '/', 'b', 'f', 'n', 'r', 't' > {}; + struct escaped : sor< escaped_char, unicode > {}; + struct unescaped : utf8::range< 0x20, 0x10FFFF > {}; + struct char_ : if_then_else< one< '\\' >, must< escaped >, unescaped > {}; + + struct string_content : until< at< one< '"' > >, must< char_ > > {}; + struct string : seq< one< '"' >, must< string_content >, any > + { + using content = string_content; + }; + + // numbers + struct minus : opt< one< '-' > > {}; + struct dot : one< '.' > {}; + + struct float_num : sor< + seq< plus< digit >, dot, star< digit > >, + seq< star< digit >, dot, plus< digit > > + > {}; + struct hex_num : seq< one< '0' >, one< 'x', 'X' >, plus< xdigit > > {}; + struct int_num : plus< digit > {}; + + struct number : seq< minus, sor< float_num, hex_num, int_num > > {}; + + // key paths + struct key_path : list< must< sor< alpha, one< '_' > >, star< sor< alnum, one< '_', '-' > > > >, one< '.' > > {}; + + // expressions and operators + struct expr : sor< string, key_path, number > {}; + struct oper : sor< one< '=' >, istring< '=', '=' >, istring< '!', '=' >, one< '<' >, istring< '<', '=' >, one< '>' >, istring< '>', '=' > > {}; + + // predicates + struct pred : seq< expr, plus< blank >, oper, plus< blank >, expr > {}; + + // rules + template< typename Rule > + struct action : nothing< Rule > {}; + template<> struct action< expr > + { + static void apply( const input & in, std::string & string_value ) + { + std::cout << in.string() << std::endl; + } + }; +} + +int main( int argc, char ** argv ) +{ + if ( argc > 1 ) { + std::string intstring; + parse< must< query::expr, eof>, query::action >( 1, argv, intstring); + } +} + diff --git a/parser/query.abnf b/parser/query.abnf new file mode 100644 index 00000000..d70416d3 --- /dev/null +++ b/parser/query.abnf @@ -0,0 +1,23 @@ + + +pred = expr 1*WSP oper 1*WSP expr +;pred =/ "(" *WSP pred *WSP ")" +;pred =/ ("NOT" / "!") 1*WSP pred +;pred =/ pred 1*WSP ("OR" / "||") !*WSP pred +;pred =/ pred 1*WSP ("AND" / "&&") 1*WSP pred + +oper = "=" / "==" / "!=" / "<" / "<=" / ">" / ">=" + +expr = string / num / key-path + +key-path = key *("." key) +key = 1*(ALPHA / "_") + +string = dq-string / sq-string +sq-string = "'" *(%x20-ffffffff) "'" +dq-string = DQUOTE *("\\" / %x20-21 / %x23-ffffffff) DQUOTE + +num = float-num / int-num / hex-num +float-num = ["-"] (*DIGIT "." 1*DIGIT / "." 1*DIGIT) +int-num = ["-"] 1*DIGIT +hex-num = ["-"] ["0"] "x" 1*HEXDIG From 93adb0cb84a2b585a2136c18698b0e6060c16f9a Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Fri, 6 Nov 2015 08:23:23 -0800 Subject: [PATCH 04/66] compound predicates --- parser/parser.cpp | 57 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 52 insertions(+), 5 deletions(-) diff --git a/parser/parser.cpp b/parser/parser.cpp index 5d89918e..ce211a21 100644 --- a/parser/parser.cpp +++ b/parser/parser.cpp @@ -2,6 +2,8 @@ #include #include +#include +#include using namespace pegtl; @@ -34,32 +36,77 @@ namespace query struct number : seq< minus, sor< float_num, hex_num, int_num > > {}; // key paths - struct key_path : list< must< sor< alpha, one< '_' > >, star< sor< alnum, one< '_', '-' > > > >, one< '.' > > {}; + struct key_path : list< seq< sor< alpha, one< '_' > >, star< sor< alnum, one< '_', '-' > > > >, one< '.' > > {}; // expressions and operators struct expr : sor< string, key_path, number > {}; - struct oper : sor< one< '=' >, istring< '=', '=' >, istring< '!', '=' >, one< '<' >, istring< '<', '=' >, one< '>' >, istring< '>', '=' > > {}; + struct oper : sor< + two< '=' >, + one< '=' >, + pegtl::string< '!', '=' >, + pegtl::string< '<', '=' >, + one< '<' >, + pegtl::string< '>', '=' >, + one< '>' > + > {}; // predicates - struct pred : seq< expr, plus< blank >, oper, plus< blank >, expr > {}; + struct comparison_pred : seq< expr, pad< oper, blank >, expr > {}; + + struct pred; + struct group_pred : if_must< one< '(' >, pad< pred, blank >, one< ')' > > {}; + + struct single_pred : pad< sor< group_pred, comparison_pred >, blank > {}; + struct not_pre : pegtl::string< 'N', 'O', 'T' > {}; + struct atom_pred : seq< opt< not_pre >, single_pred > {}; + + struct or_ext : if_must< two< '|' >, atom_pred > {}; + struct and_ext : if_must< two< '&' >, atom_pred > {}; + + struct pred : seq< atom_pred, star< sor< or_ext, and_ext > > > {}; // rules template< typename Rule > struct action : nothing< Rule > {}; - template<> struct action< expr > + template<> struct action< and_ext > + { + static void apply( const input & in, std::string & string_value ) + { + std::cout << "" << in.string() << std::endl; + } + }; + + template<> struct action< or_ext > + { + static void apply( const input & in, std::string & string_value ) + { + std::cout << "" << in.string() << std::endl; + } + }; + + template<> struct action< comparison_pred > { static void apply( const input & in, std::string & string_value ) { std::cout << in.string() << std::endl; } }; + + template<> struct action< group_pred > + { + static void apply( const input & in, std::string & string_value ) + { + std::cout << "" << std::endl; + } + }; } int main( int argc, char ** argv ) { if ( argc > 1 ) { std::string intstring; - parse< must< query::expr, eof>, query::action >( 1, argv, intstring); + analyze< query::pred >(); + parse< must< seq< query::pred, eof > >, query::action >( 1, argv, intstring); } } From 15ee92ce609e41807e856af09c18ec52ffa1e8ab Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Mon, 9 Nov 2015 08:27:27 -0800 Subject: [PATCH 05/66] full grammar --- parser/parser.cpp | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/parser/parser.cpp b/parser/parser.cpp index ce211a21..d3d1f92d 100644 --- a/parser/parser.cpp +++ b/parser/parser.cpp @@ -57,13 +57,17 @@ namespace query struct group_pred : if_must< one< '(' >, pad< pred, blank >, one< ')' > > {}; struct single_pred : pad< sor< group_pred, comparison_pred >, blank > {}; - struct not_pre : pegtl::string< 'N', 'O', 'T' > {}; + struct not_pre : sor< seq< one< '!' >, star< blank > >, seq< istring< 'N', 'O', 'T' >, plus< blank > > > {}; struct atom_pred : seq< opt< not_pre >, single_pred > {}; - struct or_ext : if_must< two< '|' >, atom_pred > {}; - struct and_ext : if_must< two< '&' >, atom_pred > {}; + struct and_op : sor< two< '&' >, istring< 'A', 'N', 'D' > > {}; + struct or_op : sor< two< '|' >, istring< 'O', 'R' > > {}; - struct pred : seq< atom_pred, star< sor< or_ext, and_ext > > > {}; + struct or_ext : seq< pad< or_op, blank >, pred > {}; + struct and_ext : seq< pad< and_op, blank >, pred > {}; + struct and_pred : seq< atom_pred, star< and_ext > > {}; + + struct pred : seq< and_pred, star< or_ext > > {}; // rules template< typename Rule > @@ -92,11 +96,27 @@ namespace query } }; + template<> struct action< one< '(' > > + { + static void apply( const input & in, std::string & string_value ) + { + std::cout << "" << std::endl; + } + }; + template<> struct action< group_pred > { static void apply( const input & in, std::string & string_value ) { - std::cout << "" << std::endl; + std::cout << "" << std::endl; + } + }; + + template<> struct action< not_pre > + { + static void apply( const input & in, std::string & string_value ) + { + std::cout << "" << std::endl; } }; } @@ -106,7 +126,7 @@ int main( int argc, char ** argv ) if ( argc > 1 ) { std::string intstring; analyze< query::pred >(); - parse< must< seq< query::pred, eof > >, query::action >( 1, argv, intstring); + parse< must< query::pred, eof >, query::action >( 1, argv, intstring); } } From 30147821a216a2674ef239c9eb258ffadbc79c07 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Mon, 9 Nov 2015 11:29:57 -0800 Subject: [PATCH 06/66] parse tree construction --- parser/main.cpp | 7 + parser/parser.cpp | 322 +++++++++++++++++++++++++++++++--------------- parser/parser.hpp | 78 +++++++++++ 3 files changed, 301 insertions(+), 106 deletions(-) create mode 100644 parser/main.cpp create mode 100644 parser/parser.hpp diff --git a/parser/main.cpp b/parser/main.cpp new file mode 100644 index 00000000..def876c3 --- /dev/null +++ b/parser/main.cpp @@ -0,0 +1,7 @@ +#include "parser.hpp" + +int main( int argc, char ** argv ) +{ + realm::parser::parse(argv[1]); +} + diff --git a/parser/parser.cpp b/parser/parser.cpp index d3d1f92d..384d748a 100644 --- a/parser/parser.cpp +++ b/parser/parser.cpp @@ -1,4 +1,23 @@ -#include +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 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 "parser.hpp" + #include #include @@ -7,126 +26,217 @@ using namespace pegtl; -namespace query +namespace realm { +namespace parser { + +// strings +struct unicode : list< seq< one< 'u' >, rep< 4, must< xdigit > > >, one< '\\' > > {}; +struct escaped_char : one< '"', '\\', '/', 'b', 'f', 'n', 'r', 't' > {}; +struct escaped : sor< escaped_char, unicode > {}; +struct unescaped : utf8::range< 0x20, 0x10FFFF > {}; +struct char_ : if_then_else< one< '\\' >, must< escaped >, unescaped > {}; + +struct string_content : until< at< one< '"' > >, must< char_ > > {}; +struct string : seq< one< '"' >, must< string_content >, any > { - // strings - struct unicode : list< seq< one< 'u' >, rep< 4, must< xdigit > > >, one< '\\' > > {}; - struct escaped_char : one< '"', '\\', '/', 'b', 'f', 'n', 'r', 't' > {}; - struct escaped : sor< escaped_char, unicode > {}; - struct unescaped : utf8::range< 0x20, 0x10FFFF > {}; - struct char_ : if_then_else< one< '\\' >, must< escaped >, unescaped > {}; + using content = string_content; +}; - struct string_content : until< at< one< '"' > >, must< char_ > > {}; - struct string : seq< one< '"' >, must< string_content >, any > - { - using content = string_content; - }; +// numbers +struct minus : opt< one< '-' > > {}; +struct dot : one< '.' > {}; - // numbers - struct minus : opt< one< '-' > > {}; - struct dot : one< '.' > {}; +struct float_num : sor< + seq< plus< digit >, dot, star< digit > >, + seq< star< digit >, dot, plus< digit > > +> {}; +struct hex_num : seq< one< '0' >, one< 'x', 'X' >, plus< xdigit > > {}; +struct int_num : plus< digit > {}; - struct float_num : sor< - seq< plus< digit >, dot, star< digit > >, - seq< star< digit >, dot, plus< digit > > - > {}; - struct hex_num : seq< one< '0' >, one< 'x', 'X' >, plus< xdigit > > {}; - struct int_num : plus< digit > {}; +struct number : seq< minus, sor< float_num, hex_num, int_num > > {}; - struct number : seq< minus, sor< float_num, hex_num, int_num > > {}; +// key paths +struct key_path : list< seq< sor< alpha, one< '_' > >, star< sor< alnum, one< '_', '-' > > > >, one< '.' > > {}; - // key paths - struct key_path : list< seq< sor< alpha, one< '_' > >, star< sor< alnum, one< '_', '-' > > > >, one< '.' > > {}; +// expressions and operators +struct expr : sor< string, key_path, number > {}; - // expressions and operators - struct expr : sor< string, key_path, number > {}; - struct oper : sor< - two< '=' >, - one< '=' >, - pegtl::string< '!', '=' >, - pegtl::string< '<', '=' >, - one< '<' >, - pegtl::string< '>', '=' >, - one< '>' > - > {}; +struct eq : sor< two< '=' >, one< '=' > > {}; +struct noteq : pegtl::string< '!', '=' > {}; +struct lteq : pegtl::string< '<', '=' > {}; +struct lt : one< '<' > {}; +struct gteq : pegtl::string< '>', '=' > {}; +struct gt : one< '<' > {}; +struct begins : istring< 'b', 'e', 'g', 'i', 'n', 's', 'w', 'i', 't', 'h' > {}; +struct ends : istring< 'e', 'n', 'd', 's', 'w', 'i', 't', 'h' > {}; +struct contains : istring< 'c', 'o', 'n', 't', 'a', 'i', 'n', 's' > {}; +struct oper : sor< eq, noteq, lteq, lt, gteq, gt, begins, ends, contains > {}; - // predicates - struct comparison_pred : seq< expr, pad< oper, blank >, expr > {}; +// predicates +struct comparison_pred : seq< expr, pad< oper, blank >, expr > {}; - struct pred; - struct group_pred : if_must< one< '(' >, pad< pred, blank >, one< ')' > > {}; +struct pred; +struct group_pred : if_must< one< '(' >, pad< pred, blank >, one< ')' > > {}; - struct single_pred : pad< sor< group_pred, comparison_pred >, blank > {}; - struct not_pre : sor< seq< one< '!' >, star< blank > >, seq< istring< 'N', 'O', 'T' >, plus< blank > > > {}; - struct atom_pred : seq< opt< not_pre >, single_pred > {}; +struct not_pre : sor< seq< one< '!' >, star< blank > >, seq< istring< 'N', 'O', 'T' >, plus< blank > > > {}; +struct atom_pred : seq< opt< not_pre >, pad< sor< group_pred, comparison_pred >, blank > > {}; - struct and_op : sor< two< '&' >, istring< 'A', 'N', 'D' > > {}; - struct or_op : sor< two< '|' >, istring< 'O', 'R' > > {}; +struct and_op : sor< two< '&' >, istring< 'A', 'N', 'D' > > {}; +struct or_op : sor< two< '|' >, istring< 'O', 'R' > > {}; - struct or_ext : seq< pad< or_op, blank >, pred > {}; - struct and_ext : seq< pad< and_op, blank >, pred > {}; - struct and_pred : seq< atom_pred, star< and_ext > > {}; +struct or_ext : seq< pad< or_op, blank >, pred > {}; +struct and_ext : seq< pad< and_op, blank >, pred > {}; +struct and_pred : seq< atom_pred, star< and_ext > > {}; - struct pred : seq< and_pred, star< or_ext > > {}; +struct pred : seq< and_pred, star< or_ext > > {}; - // rules - template< typename Rule > - struct action : nothing< Rule > {}; - template<> struct action< and_ext > - { - static void apply( const input & in, std::string & string_value ) - { - std::cout << "" << in.string() << std::endl; - } - }; - - template<> struct action< or_ext > - { - static void apply( const input & in, std::string & string_value ) - { - std::cout << "" << in.string() << std::endl; - } - }; - - template<> struct action< comparison_pred > - { - static void apply( const input & in, std::string & string_value ) - { - std::cout << in.string() << std::endl; - } - }; - - template<> struct action< one< '(' > > - { - static void apply( const input & in, std::string & string_value ) - { - std::cout << "" << std::endl; - } - }; - - template<> struct action< group_pred > - { - static void apply( const input & in, std::string & string_value ) - { - std::cout << "" << std::endl; - } - }; - - template<> struct action< not_pre > - { - static void apply( const input & in, std::string & string_value ) - { - std::cout << "" << std::endl; - } - }; -} - -int main( int argc, char ** argv ) +// state +struct ParserState { - if ( argc > 1 ) { - std::string intstring; - analyze< query::pred >(); - parse< must< query::pred, eof >, query::action >( 1, argv, intstring); + std::vector predicate_stack; + Predicate *current() { return predicate_stack.back(); } + + void addExpression(Expression exp) + { + if (current()->type == Predicate::Type::Comparison) { + current()->sub_expressions.emplace_back(std::move(exp)); + predicate_stack.pop_back(); + } + else { + Predicate p; + p.type = Predicate::Type::Comparison; + p.sub_expressions.emplace_back(std::move(exp)); + current()->sub_predicates.emplace_back(std::move(p)); + predicate_stack.push_back(¤t()->sub_predicates.back()); + } } +}; + +// rules +template< typename Rule > +struct action : nothing< Rule > {}; + +template<> struct action< and_ext > +{ + static void apply( const input & in, ParserState & state ) + { + std::cout << "" << in.string() << std::endl; + } +}; + +template<> struct action< or_ext > +{ + static void apply( const input & in, ParserState & state ) + { + std::cout << "" << in.string() << std::endl; + } +}; + +template<> struct action< string > +{ + static void apply( const input & in, ParserState & state ) + { + std::cout << in.string() << std::endl; + + Expression exp; + exp.type = Expression::Type::String; + exp.s = in.string(); + + state.addExpression(exp); + } +}; + +template<> struct action< key_path > +{ + static void apply( const input & in, ParserState & state ) + { + std::cout << in.string() << std::endl; + + Expression exp; + exp.type = Expression::Type::KeyPath; + exp.s = in.string(); + + state.addExpression(std::move(exp)); + } +}; + +template<> struct action< number > +{ + static void apply( const input & in, ParserState & state ) + { + std::cout << in.string() << std::endl; + + Expression exp; + exp.type = Expression::Type::Number; + exp.s = in.string(); + + state.addExpression(std::move(exp)); + } +}; + +#define OPERATOR_ACTION(rule, oper) \ +template<> struct action< rule > { \ + static void apply( const input & in, ParserState & state ) { \ + std::cout << in.string() << std::endl; \ + state.current()->op = oper; }}; + +OPERATOR_ACTION(eq, Predicate::Operator::Equal) +OPERATOR_ACTION(noteq, Predicate::Operator::NotEqual) +OPERATOR_ACTION(gteq, Predicate::Operator::GreaterThanOrEqual) +OPERATOR_ACTION(gt, Predicate::Operator::GreaterThan) +OPERATOR_ACTION(lteq, Predicate::Operator::LessThanOrEqual) +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< one< '(' > > +{ + static void apply( const input & in, ParserState & state ) + { + std::cout << "" << std::endl; + + Predicate group; + group.type = Predicate::Type::And; + state.current()->sub_predicates.emplace_back(std::move(group)); + state.predicate_stack.push_back(&state.current()->sub_predicates.back()); + } +}; + +template<> struct action< group_pred > +{ + static void apply( const input & in, ParserState & state ) + { + std::cout << "" << std::endl; + + state.predicate_stack.pop_back(); + } +}; + +template<> struct action< not_pre > +{ + static void apply( const input & in, ParserState & state ) + { + std::cout << "" << std::endl; + } +}; + +Predicate parse(const std::string &query) +{ + analyze< pred >(); + const std::string source = "user query"; + + Predicate out_predicate; + out_predicate.type = Predicate::Type::And; + + ParserState state; + state.predicate_stack.push_back(&out_predicate); + + pegtl::parse< must< pred, eof >, action >(query, source, state); + return out_predicate; } +}} + + diff --git a/parser/parser.hpp b/parser/parser.hpp new file mode 100644 index 00000000..416faa2e --- /dev/null +++ b/parser/parser.hpp @@ -0,0 +1,78 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 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. +// +//////////////////////////////////////////////////////////////////////////// + +#ifndef REALM_EXTERNAL_COMMIT_HELPER_HPP +#define REALM_EXTERNAL_COMMIT_HELPER_HPP + +#include +#include + +namespace realm { + namespace parser { + struct Expression + { + enum class Type + { + Number, + String, + KeyPath, + }; + Type type; + + std::string s; + }; + + struct Predicate + { + enum class Type + { + None, + Comparison, + Or, + And, + True, + False, + }; + Type type = Type::None; + + // for comparison + enum class Operator + { + None, + Equal, + NotEqual, + LessThan, + LessThanOrEqual, + GreaterThan, + GreaterThanOrEqual, + BeginsWith, + EndsWith, + Contains, + }; + Operator op = Operator::None; + std::vector sub_expressions; + + // for compounds + std::vector sub_predicates; + }; + + Predicate parse(const std::string &query); + } +} + +#endif // REALM_EXTERNAL_COMMIT_HELPER_HPP From 51f5a422fd356efd82bfd6cf28faa9796ed4080c Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Mon, 9 Nov 2015 12:13:01 -0800 Subject: [PATCH 07/66] support OR with proper precedence --- parser/parser.cpp | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/parser/parser.cpp b/parser/parser.cpp index 384d748a..ab2b8726 100644 --- a/parser/parser.cpp +++ b/parser/parser.cpp @@ -121,6 +121,28 @@ template<> struct action< and_ext > static void apply( const input & in, ParserState & state ) { std::cout << "" << in.string() << std::endl; + + // if we were put into an OR group we need to rearrange + auto current = state.current(); + if (current->type == Predicate::Type::Or) { + auto &sub_preds = state.current()->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.sub_predicates.push_back(sub_preds.back()); + sub_preds.pop_back(); + } + else { + // otherwise combine last two into a new AND group + Predicate pred; + pred.type = Predicate::Type::And; + pred.sub_predicates = { second_last, sub_preds.back() }; + sub_preds.pop_back(); + sub_preds.pop_back(); + sub_preds.push_back(pred); + } + } } }; @@ -129,6 +151,29 @@ template<> struct action< or_ext > static void apply( const input & in, ParserState & state ) { std::cout << "" << in.string() << std::endl; + + // if already an OR group do nothing + auto current = 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()->sub_predicates; + if (sub_preds.size()) { + current->type = Predicate::Type::Or; + return; + } + + // split the current group into to groups which are ORed together + Predicate pred1, pred2; + pred1.type = Predicate::Type::And; + pred2.type = Predicate::Type::And; + pred1.sub_predicates.insert(sub_preds.begin(), sub_preds.back()); + pred2.sub_predicates.push_back(sub_preds.back()); + + current->type = Predicate::Type::Or; + sub_preds = { pred1, pred2 }; } }; From d59e6b1f58edd60428f7e21f516854493a2e9ceb Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Mon, 9 Nov 2015 12:20:59 -0800 Subject: [PATCH 08/66] store negated predicates in parse tree --- parser/parser.cpp | 12 ++++++++++++ parser/parser.hpp | 2 ++ 2 files changed, 14 insertions(+) diff --git a/parser/parser.cpp b/parser/parser.cpp index ab2b8726..3b504ee9 100644 --- a/parser/parser.cpp +++ b/parser/parser.cpp @@ -95,6 +95,7 @@ struct ParserState { std::vector predicate_stack; Predicate *current() { return predicate_stack.back(); } + bool negate_next; void addExpression(Expression exp) { @@ -106,6 +107,10 @@ struct ParserState Predicate p; p.type = Predicate::Type::Comparison; p.sub_expressions.emplace_back(std::move(exp)); + if (negate_next) { + p.negate = true; + negate_next = false; + } current()->sub_predicates.emplace_back(std::move(p)); predicate_stack.push_back(¤t()->sub_predicates.back()); } @@ -244,6 +249,11 @@ template<> struct action< one< '(' > > Predicate group; group.type = Predicate::Type::And; + if (state.negate_next) { + group.negate = true; + state.negate_next = false; + } + state.current()->sub_predicates.emplace_back(std::move(group)); state.predicate_stack.push_back(&state.current()->sub_predicates.back()); } @@ -264,6 +274,8 @@ template<> struct action< not_pre > static void apply( const input & in, ParserState & state ) { std::cout << "" << std::endl; + + state.negate_next = true; } }; diff --git a/parser/parser.hpp b/parser/parser.hpp index 416faa2e..149f12ff 100644 --- a/parser/parser.hpp +++ b/parser/parser.hpp @@ -69,6 +69,8 @@ namespace realm { // for compounds std::vector sub_predicates; + + bool negate; }; Predicate parse(const std::string &query); From 1f78bf7db686041a5489e4fb98adee6b26c6a4bb Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Mon, 9 Nov 2015 21:12:18 -0800 Subject: [PATCH 09/66] hook it up --- parser/parser.cpp | 351 ++++++++++++++++++++++++++++++++++++++++++++++ parser/parser.hpp | 5 + parser/query.abnf | 8 +- 3 files changed, 360 insertions(+), 4 deletions(-) diff --git a/parser/parser.cpp b/parser/parser.cpp index 3b504ee9..723332b1 100644 --- a/parser/parser.cpp +++ b/parser/parser.cpp @@ -24,6 +24,10 @@ #include #include +#include +#include "object_store.hpp" +#include "schema.hpp" + using namespace pegtl; namespace realm { @@ -291,9 +295,356 @@ Predicate parse(const std::string &query) state.predicate_stack.push_back(&out_predicate); pegtl::parse< must< pred, eof >, action >(query, source, state); + if (out_predicate.type == Predicate::Type::And && out_predicate.sub_predicates.size() == 1) { + return out_predicate.sub_predicates.back(); + } return out_predicate; } + + +// check a precondition and throw an exception if it is not met +// this should be used iff the condition being false indicates a bug in the caller +// of the function checking its preconditions +static void precondition(bool condition, const std::string message) { + if (__builtin_expect(condition, 1)) { + return; + } + throw std::runtime_error(message); +} + +// FIXME: TrueExpression and FalseExpression should be supported by core in some way +struct TrueExpression : realm::Expression { + size_t find_first(size_t start, size_t end) const override + { + if (start != end) + return start; + + return not_found; + } + void set_table() override {} + const Table* get_table() const override { return nullptr; } +}; + +struct FalseExpression : realm::Expression { + size_t find_first(size_t, size_t) const override { return not_found; } + void set_table() override {} + const Table* get_table() const override { return nullptr; } +}; + + +// add a clause for numeric constraints based on operator type +template +void add_numeric_constraint_to_query(Query& query, + PropertyType datatype, + Predicate::Operator operatorType, + A lhs, + B rhs) +{ + switch (operatorType) { + case Predicate::Operator::LessThan: + query.and_query(lhs < rhs); + break; + case Predicate::Operator::LessThanOrEqual: + query.and_query(lhs <= rhs); + break; + case Predicate::Operator::GreaterThan: + query.and_query(lhs > rhs); + break; + case Predicate::Operator::GreaterThanOrEqual: + query.and_query(lhs >= rhs); + break; + case Predicate::Operator::Equal: + query.and_query(lhs == rhs); + break; + case Predicate::Operator::NotEqual: + query.and_query(lhs != rhs); + break; + default: + throw std::runtime_error("Unsupported operator for numeric queries."); + } +} + +template +void add_bool_constraint_to_query(Query &query, Predicate::Operator operatorType, A lhs, B rhs) { + switch (operatorType) { + case Predicate::Operator::Equal: + query.and_query(lhs == rhs); + break; + case Predicate::Operator::NotEqual: + query.and_query(lhs != rhs); + break; + default: + throw std::runtime_error("Unsupported operator for numeric queries."); + } +} + +void add_string_constraint_to_query(Query &query, + Predicate::Operator op, + Columns &&column, + StringData value) { + bool case_sensitive = true; + StringData sd = value; + switch (op) { + case Predicate::Operator::BeginsWith: + query.and_query(column.begins_with(sd, case_sensitive)); + break; + case Predicate::Operator::EndsWith: + query.and_query(column.ends_with(sd, case_sensitive)); + break; + case Predicate::Operator::Contains: + query.and_query(column.contains(sd, case_sensitive)); + break; + case Predicate::Operator::Equal: + query.and_query(column.equal(sd, case_sensitive)); + break; + case Predicate::Operator::NotEqual: + query.and_query(column.not_equal(sd, case_sensitive)); + break; + default: + throw std::runtime_error("Unsupported operator for string queries."); + } +} + +void add_string_constraint_to_query(realm::Query& query, + Predicate::Operator op, + StringData value, + Columns &&column) { + bool case_sensitive = true; + StringData sd = value; + switch (op) { + case Predicate::Operator::Equal: + query.and_query(column.equal(sd, case_sensitive)); + break; + case Predicate::Operator::NotEqual: + query.and_query(column.not_equal(sd, case_sensitive)); + break; + default: + throw std::runtime_error("Substring comparison not supported for keypath substrings."); + } +} + +template +struct ColumnOfTypeHelper { + static Columns convert(TableGetter&& table, unsigned int idx) + { + return table()->template column(idx); + } +}; + +template +struct ColumnOfTypeHelper { + static Columns convert(TableGetter&& table, unsigned int idx) + { + return table()->template column(idx); + } +}; + +template +struct ValueOfTypeHelper; + +template +struct ValueOfTypeHelper { + static Int convert(TableGetter&&, const std::string & value) + { + assert(0); + } +}; + +template +struct ValueOfTypeHelper { + static bool convert(TableGetter&&, const std::string & value) + { + assert(0); + } +}; + +template +struct ValueOfTypeHelper { + static Double convert(TableGetter&&, const std::string & value) + { + return std::stod(value); + } +}; + +template +struct ValueOfTypeHelper { + static Float convert(TableGetter&&, const std::string & value) + { + return std::stof(value); + } +}; + +template +struct ValueOfTypeHelper { + static Int convert(TableGetter&&, const std::string & value) + { + return std::stoll(value); + } +}; + +template +struct ValueOfTypeHelper { + static std::string convert(TableGetter&&, const std::string & value) + { + return value; + } +}; + +template +auto value_of_type_for_query(TableGetter&& tables, Value&& value) +{ + const bool isColumnIndex = std::is_same::type>::value; + using helper = std::conditional_t, + ValueOfTypeHelper>; + return helper::convert(std::forward(tables), std::forward(value)); +} + +std::vector &split(const std::string &s, char delim, std::vector &elems) { + std::stringstream ss(s); + std::string item; + while (std::getline(ss, item, delim)) { + elems.push_back(item); + } + return elems; +} + +std::vector split(const std::string &s, char delim) { + std::vector elems; + split(s, delim, elems); + return elems; +} + +Property *get_property_from_key_path(Schema &schema, ObjectSchema &desc, const std::string &key_path, std::vector &indexes) +{ + Property *prop = nullptr; + + auto paths = split(key_path, '.'); + for (size_t index = 0; index < paths.size(); index++) { + prop = desc.property_for_name(paths[index]); + precondition(prop != nullptr, "No property '" + paths[index] + "' on object of type '" + desc.name + "'"); + precondition(index == paths.size() - 1 || prop->type == PropertyTypeObject || prop->type == PropertyTypeArray, + (std::string)"Property '" + paths[index] + "' is not a link in object of type '" + desc.name + "'"); + + indexes.push_back(prop->table_column); + desc = *schema.find(prop->object_type); + } + return prop; +} + +template + void do_add_comparison_to_query(Query &query, Schema &schema, ObjectSchema &object_schema, Property *prop, + Predicate::Operator op, const std::vector& indexes, T... values) +{ + auto table = [&] { + TableRef& tbl = query.get_table(); + for (size_t col : indexes) { + tbl->link(col); // mutates m_link_chain on table + } + return tbl.get(); + }; + + auto type = prop->type; + switch (type) { + case PropertyTypeBool: + add_bool_constraint_to_query(query, op, value_of_type_for_query(table, values)...); + break; + case PropertyTypeDate: + add_numeric_constraint_to_query(query, type, op, value_of_type_for_query(table, values)...); + break; + case PropertyTypeDouble: + add_numeric_constraint_to_query(query, type, op, value_of_type_for_query(table, values)...); + break; + case PropertyTypeFloat: + add_numeric_constraint_to_query(query, type, op, value_of_type_for_query(table, values)...); + break; + case PropertyTypeInt: + add_numeric_constraint_to_query(query, type, op, value_of_type_for_query(table, values)...); + break; + case PropertyTypeString: + case PropertyTypeData: + add_string_constraint_to_query(query, op, value_of_type_for_query(table, values)...); + break; + default: { + throw std::runtime_error((std::string)"Object type " + string_for_property_type(type) + " not supported"); + } + } +} + +void add_comparison_to_query(Query &query, Predicate &pred, Schema &schema, ObjectSchema &object_schema) +{ + std::vector indexes; + auto t0 = pred.sub_expressions[0].type, t1 = pred.sub_expressions[1].type; + if (t0 == Expression::Type::KeyPath && t1 != Expression::Type::KeyPath) { + Property *prop = get_property_from_key_path(schema, object_schema, pred.sub_expressions[0].s, indexes); + do_add_comparison_to_query(query, schema, object_schema, prop, pred.op, indexes, prop->table_column, pred.sub_expressions[1].s); + } + else if (t0 != Expression::Type::KeyPath && t1 == Expression::Type::KeyPath) { + Property *prop = get_property_from_key_path(schema, object_schema, pred.sub_expressions[1].s, indexes); + do_add_comparison_to_query(query, schema, object_schema, prop, pred.op, indexes, pred.sub_expressions[0].s, prop->table_column); + } + else { + throw std::runtime_error("Predicate expressions must compare a keypath and another keypath or a constant value"); + } +} + +void update_query_with_predicate(Query &query, Predicate &pred, Schema &schema, ObjectSchema &object_schema) +{ + if (pred.negate) { + query.Not(); + } + + switch (pred.type) { + case Predicate::Type::And: + query.group(); + for (auto &sub : pred.sub_predicates) { + update_query_with_predicate(query, sub, schema, object_schema); + } + if (!pred.sub_predicates.size()) { + query.and_query(new TrueExpression); + } + query.end_group(); + break; + + case Predicate::Type::Or: + query.group(); + for (auto &sub : pred.sub_predicates) { + query.Or(); + update_query_with_predicate(query, sub, schema, object_schema); + } + if (!pred.sub_predicates.size()) { + query.and_query(new FalseExpression); + } + query.end_group(); + break; + + case Predicate::Type::Comparison: { + add_comparison_to_query(query, pred, schema, object_schema); + break; + } + case Predicate::Type::True: + query.and_query(new TrueExpression); + break; + + case Predicate::Type::False: + query.and_query(new FalseExpression); + break; + + default: + throw std::runtime_error("Invalid predicate type"); + break; + } +} + +void apply_predicate(Query &query, Predicate &predicate, Schema &schema, std::string objectType) { + update_query_with_predicate(query, predicate, schema, *schema.find(objectType)); + + // Test the constructed query in core + std::string validateMessage = query.validate(); + precondition(validateMessage.empty(), validateMessage.c_str()); +} + }} diff --git a/parser/parser.hpp b/parser/parser.hpp index 149f12ff..99dda99d 100644 --- a/parser/parser.hpp +++ b/parser/parser.hpp @@ -23,6 +23,9 @@ #include namespace realm { + class Query; + class Schema; + namespace parser { struct Expression { @@ -74,6 +77,8 @@ namespace realm { }; Predicate parse(const std::string &query); + + void apply_predicate(Query &query, Predicate &predicate, Schema &schema, std::string objectType); } } diff --git a/parser/query.abnf b/parser/query.abnf index d70416d3..d315fd35 100644 --- a/parser/query.abnf +++ b/parser/query.abnf @@ -17,7 +17,7 @@ string = dq-string / sq-string sq-string = "'" *(%x20-ffffffff) "'" dq-string = DQUOTE *("\\" / %x20-21 / %x23-ffffffff) DQUOTE -num = float-num / int-num / hex-num -float-num = ["-"] (*DIGIT "." 1*DIGIT / "." 1*DIGIT) -int-num = ["-"] 1*DIGIT -hex-num = ["-"] ["0"] "x" 1*HEXDIG +num = ["-"] (float-num / int-num / hex-num) +float-num = (*DIGIT "." 1*DIGIT / "." 1*DIGIT) +int-num = 1*DIGIT +hex-num = ("0x" / "0X") 1*HEXDIG From d654b7d52cb6025d805f4de86e0230cfc35dbbec Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Mon, 9 Nov 2015 22:08:06 -0800 Subject: [PATCH 10/66] support for truepredicate/falsepredicate, single quote strings - all tests now pass --- parser/parser.cpp | 77 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 60 insertions(+), 17 deletions(-) diff --git a/parser/parser.cpp b/parser/parser.cpp index 723332b1..770cab77 100644 --- a/parser/parser.cpp +++ b/parser/parser.cpp @@ -35,16 +35,16 @@ namespace parser { // strings struct unicode : list< seq< one< 'u' >, rep< 4, must< xdigit > > >, one< '\\' > > {}; -struct escaped_char : one< '"', '\\', '/', 'b', 'f', 'n', 'r', 't' > {}; +struct escaped_char : one< '"', '\'', '\\', '/', 'b', 'f', 'n', 'r', 't' > {}; struct escaped : sor< escaped_char, unicode > {}; struct unescaped : utf8::range< 0x20, 0x10FFFF > {}; struct char_ : if_then_else< one< '\\' >, must< escaped >, unescaped > {}; -struct string_content : until< at< one< '"' > >, must< char_ > > {}; -struct string : seq< one< '"' >, must< string_content >, any > -{ - using content = string_content; -}; +struct dq_string_content : until< at< one< '"' > >, must< char_ > > {}; +struct dq_string : seq< one< '"' >, must< dq_string_content >, any > {}; + +struct sq_string_content : until< at< one< '\'' > >, must< char_ > > {}; +struct sq_string : seq< one< '\'' >, must< sq_string_content >, any > {}; // numbers struct minus : opt< one< '-' > > {}; @@ -63,17 +63,17 @@ struct number : seq< minus, sor< float_num, hex_num, int_num > > {}; struct key_path : list< seq< sor< alpha, one< '_' > >, star< sor< alnum, one< '_', '-' > > > >, one< '.' > > {}; // expressions and operators -struct expr : sor< string, key_path, number > {}; +struct expr : sor< dq_string, sq_string, key_path, number > {}; struct eq : sor< two< '=' >, one< '=' > > {}; struct noteq : pegtl::string< '!', '=' > {}; struct lteq : pegtl::string< '<', '=' > {}; struct lt : one< '<' > {}; struct gteq : pegtl::string< '>', '=' > {}; -struct gt : one< '<' > {}; -struct begins : istring< 'b', 'e', 'g', 'i', 'n', 's', 'w', 'i', 't', 'h' > {}; -struct ends : istring< 'e', 'n', 'd', 's', 'w', 'i', 't', 'h' > {}; -struct contains : istring< 'c', 'o', 'n', 't', 'a', 'i', 'n', 's' > {}; +struct gt : one< '>' > {}; +struct begins : istring< 'b','e','g','i','n','s','w','i','t','h' > {}; +struct ends : istring< 'e','n','d','s','w','i','t','h' > {}; +struct contains : istring< 'c','o','n','t','a','i','n','s' > {}; struct oper : sor< eq, noteq, lteq, lt, gteq, gt, begins, ends, contains > {}; // predicates @@ -81,9 +81,11 @@ struct comparison_pred : seq< expr, pad< oper, blank >, expr > {}; struct pred; struct group_pred : if_must< one< '(' >, pad< pred, blank >, one< ')' > > {}; +struct true_pred : sor< istring<'t','r','u','e','p','r','e','d','i','c','a','t','e'>, istring<'t','r','u','e'> > {}; +struct false_pred : sor< istring<'f','a','l','s','e','p','r','e','d','i','c','a','t','e'>, istring<'f','a','l','s','e'> > {}; struct not_pre : sor< seq< one< '!' >, star< blank > >, seq< istring< 'N', 'O', 'T' >, plus< blank > > > {}; -struct atom_pred : seq< opt< not_pre >, pad< sor< group_pred, comparison_pred >, blank > > {}; +struct atom_pred : seq< opt< not_pre >, pad< sor< group_pred, true_pred, false_pred, comparison_pred >, blank > > {}; struct and_op : sor< two< '&' >, istring< 'A', 'N', 'D' > > {}; struct or_op : sor< two< '|' >, istring< 'O', 'R' > > {}; @@ -186,7 +188,21 @@ template<> struct action< or_ext > } }; -template<> struct action< string > +template<> struct action< dq_string_content > +{ + static void apply( const input & in, ParserState & state ) + { + std::cout << in.string() << std::endl; + + Expression exp; + exp.type = Expression::Type::String; + exp.s = in.string(); + + state.addExpression(exp); + } +}; + +template<> struct action< sq_string_content > { static void apply( const input & in, ParserState & state ) { @@ -228,6 +244,28 @@ template<> struct action< number > } }; +template<> struct action< true_pred > +{ + static void apply( const input & in, ParserState & state ) + { + std::cout << in.string() << std::endl; + Predicate pred; + pred.type = Predicate::Type::True; + state.current()->sub_predicates.push_back(std::move(pred)); + } +}; + +template<> struct action< false_pred > +{ + static void apply( const input & in, ParserState & state ) + { + std::cout << in.string() << std::endl; + Predicate pred; + pred.type = Predicate::Type::False; + state.current()->sub_predicates.push_back(std::move(pred)); + } +}; + #define OPERATOR_ACTION(rule, oper) \ template<> struct action< rule > { \ static void apply( const input & in, ParserState & state ) { \ @@ -522,13 +560,18 @@ Property *get_property_from_key_path(Schema &schema, ObjectSchema &desc, const s auto paths = split(key_path, '.'); for (size_t index = 0; index < paths.size(); index++) { + if (prop) { + precondition(prop->type == PropertyTypeObject || prop->type == PropertyTypeArray, + (std::string)"Property '" + paths[index] + "' is not a link in object of type '" + desc.name + "'"); + indexes.push_back(prop->table_column); + + } prop = desc.property_for_name(paths[index]); precondition(prop != nullptr, "No property '" + paths[index] + "' on object of type '" + desc.name + "'"); - precondition(index == paths.size() - 1 || prop->type == PropertyTypeObject || prop->type == PropertyTypeArray, - (std::string)"Property '" + paths[index] + "' is not a link in object of type '" + desc.name + "'"); - indexes.push_back(prop->table_column); - desc = *schema.find(prop->object_type); + if (prop->object_type.size()) { + desc = *schema.find(prop->object_type); + } } return prop; } From fad667f844fe716e7687bcd3c4fe9b4a04330a26 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Tue, 10 Nov 2015 09:09:20 -0800 Subject: [PATCH 11/66] code cleanup, bugfixes --- parser/parser.cpp | 105 ++++++++++++++++++++++------------------------ parser/parser.hpp | 44 ++++++++++--------- 2 files changed, 72 insertions(+), 77 deletions(-) diff --git a/parser/parser.cpp b/parser/parser.cpp index 770cab77..ba4fd68f 100644 --- a/parser/parser.cpp +++ b/parser/parser.cpp @@ -100,25 +100,26 @@ struct pred : seq< and_pred, star< or_ext > > {}; struct ParserState { std::vector predicate_stack; - Predicate *current() { return predicate_stack.back(); } + Predicate ¤t() { + return *predicate_stack.back(); + } bool negate_next; void addExpression(Expression exp) { - if (current()->type == Predicate::Type::Comparison) { - current()->sub_expressions.emplace_back(std::move(exp)); + if (current().type == Predicate::Type::Comparison) { + current().cmpr.expr[1] = std::move(exp); predicate_stack.pop_back(); } else { - Predicate p; - p.type = Predicate::Type::Comparison; - p.sub_expressions.emplace_back(std::move(exp)); + Predicate p(Predicate::Type::Comparison); + p.cmpr.expr[0] = std::move(exp); if (negate_next) { p.negate = true; negate_next = false; } - current()->sub_predicates.emplace_back(std::move(p)); - predicate_stack.push_back(¤t()->sub_predicates.back()); + current().cpnd.sub_predicates.emplace_back(std::move(p)); + predicate_stack.push_back(¤t().cpnd.sub_predicates.back()); } } }; @@ -134,24 +135,24 @@ template<> struct action< and_ext > std::cout << "" << in.string() << std::endl; // if we were put into an OR group we need to rearrange - auto current = state.current(); - if (current->type == Predicate::Type::Or) { - auto &sub_preds = state.current()->sub_predicates; - auto second_last = sub_preds[sub_preds.size()-2]; + auto ¤t = state.current(); + if (current.type == Predicate::Type::Or) { + auto &sub_preds = state.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.sub_predicates.push_back(sub_preds.back()); + 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; - pred.type = Predicate::Type::And; - pred.sub_predicates = { second_last, sub_preds.back() }; + 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(pred); + sub_preds.push_back(std::move(pred)); } } } @@ -164,27 +165,27 @@ template<> struct action< or_ext > std::cout << "" << in.string() << std::endl; // if already an OR group do nothing - auto current = state.current(); - if (current->type == Predicate::Type::Or) { + 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()->sub_predicates; + auto &sub_preds = state.current().cpnd.sub_predicates; if (sub_preds.size()) { - current->type = Predicate::Type::Or; + current.type = Predicate::Type::Or; return; } // split the current group into to groups which are ORed together - Predicate pred1, pred2; - pred1.type = Predicate::Type::And; - pred2.type = Predicate::Type::And; - pred1.sub_predicates.insert(sub_preds.begin(), sub_preds.back()); - pred2.sub_predicates.push_back(sub_preds.back()); + 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())); - current->type = Predicate::Type::Or; - sub_preds = { pred1, pred2 }; + current.type = Predicate::Type::Or; + sub_preds.clear(); + sub_preds.emplace_back(std::move(pred1)); + sub_preds.emplace_back(std::move(pred2)); } }; @@ -249,9 +250,8 @@ template<> struct action< true_pred > static void apply( const input & in, ParserState & state ) { std::cout << in.string() << std::endl; - Predicate pred; - pred.type = Predicate::Type::True; - state.current()->sub_predicates.push_back(std::move(pred)); + Predicate pred(Predicate::Type::True); + state.current().cpnd.sub_predicates.push_back(std::move(pred)); } }; @@ -260,9 +260,8 @@ template<> struct action< false_pred > static void apply( const input & in, ParserState & state ) { std::cout << in.string() << std::endl; - Predicate pred; - pred.type = Predicate::Type::False; - state.current()->sub_predicates.push_back(std::move(pred)); + Predicate pred(Predicate::Type::False); + state.current().cpnd.sub_predicates.push_back(std::move(pred)); } }; @@ -270,7 +269,7 @@ template<> struct action< false_pred > template<> struct action< rule > { \ static void apply( const input & in, ParserState & state ) { \ std::cout << in.string() << std::endl; \ - state.current()->op = oper; }}; + state.current().cmpr.op = oper; }}; OPERATOR_ACTION(eq, Predicate::Operator::Equal) OPERATOR_ACTION(noteq, Predicate::Operator::NotEqual) @@ -289,15 +288,14 @@ template<> struct action< one< '(' > > { std::cout << "" << std::endl; - Predicate group; - group.type = Predicate::Type::And; + Predicate group(Predicate::Type::And); if (state.negate_next) { group.negate = true; state.negate_next = false; } - state.current()->sub_predicates.emplace_back(std::move(group)); - state.predicate_stack.push_back(&state.current()->sub_predicates.back()); + state.current().cpnd.sub_predicates.emplace_back(std::move(group)); + state.predicate_stack.push_back(&state.current().cpnd.sub_predicates.back()); } }; @@ -326,21 +324,19 @@ Predicate parse(const std::string &query) analyze< pred >(); const std::string source = "user query"; - Predicate out_predicate; - out_predicate.type = Predicate::Type::And; + Predicate out_predicate(Predicate::Type::And); ParserState state; state.predicate_stack.push_back(&out_predicate); pegtl::parse< must< pred, eof >, action >(query, source, state); - if (out_predicate.type == Predicate::Type::And && out_predicate.sub_predicates.size() == 1) { - return out_predicate.sub_predicates.back(); + if (out_predicate.type == Predicate::Type::And && out_predicate.cpnd.sub_predicates.size() == 1) { + return std::move(out_predicate.cpnd.sub_predicates.back()); } - return out_predicate; + return std::move(out_predicate); } - // check a precondition and throw an exception if it is not met // this should be used iff the condition being false indicates a bug in the caller // of the function checking its preconditions @@ -618,14 +614,15 @@ template void add_comparison_to_query(Query &query, Predicate &pred, Schema &schema, ObjectSchema &object_schema) { std::vector indexes; - auto t0 = pred.sub_expressions[0].type, t1 = pred.sub_expressions[1].type; + Predicate::Comparison &cmpr = pred.cmpr; + auto t0 = cmpr.expr[0].type, t1 = cmpr.expr[1].type; if (t0 == Expression::Type::KeyPath && t1 != Expression::Type::KeyPath) { - Property *prop = get_property_from_key_path(schema, object_schema, pred.sub_expressions[0].s, indexes); - do_add_comparison_to_query(query, schema, object_schema, prop, pred.op, indexes, prop->table_column, pred.sub_expressions[1].s); + Property *prop = get_property_from_key_path(schema, object_schema, cmpr.expr[0].s, indexes); + do_add_comparison_to_query(query, schema, object_schema, prop, cmpr.op, indexes, prop->table_column, cmpr.expr[1].s); } else if (t0 != Expression::Type::KeyPath && t1 == Expression::Type::KeyPath) { - Property *prop = get_property_from_key_path(schema, object_schema, pred.sub_expressions[1].s, indexes); - do_add_comparison_to_query(query, schema, object_schema, prop, pred.op, indexes, pred.sub_expressions[0].s, prop->table_column); + Property *prop = get_property_from_key_path(schema, object_schema, cmpr.expr[1].s, indexes); + do_add_comparison_to_query(query, schema, object_schema, prop, cmpr.op, indexes, cmpr.expr[0].s, prop->table_column); } else { throw std::runtime_error("Predicate expressions must compare a keypath and another keypath or a constant value"); @@ -641,10 +638,10 @@ void update_query_with_predicate(Query &query, Predicate &pred, Schema &schema, switch (pred.type) { case Predicate::Type::And: query.group(); - for (auto &sub : pred.sub_predicates) { + for (auto &sub : pred.cpnd.sub_predicates) { update_query_with_predicate(query, sub, schema, object_schema); } - if (!pred.sub_predicates.size()) { + if (!pred.cpnd.sub_predicates.size()) { query.and_query(new TrueExpression); } query.end_group(); @@ -652,11 +649,11 @@ void update_query_with_predicate(Query &query, Predicate &pred, Schema &schema, case Predicate::Type::Or: query.group(); - for (auto &sub : pred.sub_predicates) { + for (auto &sub : pred.cpnd.sub_predicates) { query.Or(); update_query_with_predicate(query, sub, schema, object_schema); } - if (!pred.sub_predicates.size()) { + if (!pred.cpnd.sub_predicates.size()) { query.and_query(new FalseExpression); } query.end_group(); diff --git a/parser/parser.hpp b/parser/parser.hpp index 99dda99d..c53887f5 100644 --- a/parser/parser.hpp +++ b/parser/parser.hpp @@ -29,33 +29,21 @@ namespace realm { namespace parser { struct Expression { - enum class Type - { - Number, - String, - KeyPath, - }; - Type type; - + enum class Type { Number, String, KeyPath } type; std::string s; }; struct Predicate { - enum class Type - { - None, + enum class Type { Comparison, Or, And, True, - False, - }; - Type type = Type::None; + False + } type = Type::And; - // for comparison - enum class Operator - { + enum class Operator { None, Equal, NotEqual, @@ -65,15 +53,25 @@ namespace realm { GreaterThanOrEqual, BeginsWith, EndsWith, - Contains, + Contains }; - Operator op = Operator::None; - std::vector sub_expressions; - // for compounds - std::vector sub_predicates; + struct Comparison { + Operator op = Operator::None; + Expression expr[2]; + ~Comparison() {} + }; - bool negate; + struct Compound { + std::vector sub_predicates; + }; + + Comparison cmpr; + Compound cpnd; + + bool negate = false; + + Predicate(Type t) : type(t) {} }; Predicate parse(const std::string &query); From 39956b910ba29c390378cced379e62593b88310a Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Tue, 10 Nov 2015 11:26:27 -0800 Subject: [PATCH 12/66] remove duplicate code, add argument expression type --- parser/parser.cpp | 82 ++++++++++++----------------------------------- parser/parser.hpp | 4 ++- 2 files changed, 24 insertions(+), 62 deletions(-) diff --git a/parser/parser.cpp b/parser/parser.cpp index ba4fd68f..be632cac 100644 --- a/parser/parser.cpp +++ b/parser/parser.cpp @@ -62,8 +62,12 @@ struct number : seq< minus, sor< float_num, hex_num, int_num > > {}; // key paths struct key_path : list< seq< sor< alpha, one< '_' > >, star< sor< alnum, one< '_', '-' > > > >, one< '.' > > {}; +// argument +struct argument_index : until< at< one< '}' > >, must< digit > > {}; +struct argument : seq< one< '{' >, must< argument_index >, any > {}; + // expressions and operators -struct expr : sor< dq_string, sq_string, key_path, number > {}; +struct expr : sor< dq_string, sq_string, key_path, number, argument > {}; struct eq : sor< two< '=' >, one< '=' > > {}; struct noteq : pegtl::string< '!', '=' > {}; @@ -103,9 +107,10 @@ struct ParserState Predicate ¤t() { return *predicate_stack.back(); } - bool negate_next; - void addExpression(Expression exp) + bool negate_next = false; + + void addExpression(Expression && exp) { if (current().type == Predicate::Type::Comparison) { current().cmpr.expr[1] = std::move(exp); @@ -189,69 +194,26 @@ template<> struct action< or_ext > } }; -template<> struct action< dq_string_content > -{ - static void apply( const input & in, ParserState & state ) - { - std::cout << in.string() << std::endl; - Expression exp; - exp.type = Expression::Type::String; - exp.s = in.string(); +#define EXPRESSION_ACTION(rule, type) \ +template<> struct action< rule > { \ + static void apply( const input & in, ParserState & state ) { \ + std::cout << in.string() << std::endl; \ + state.addExpression(Expression(type, in.string())); }}; - state.addExpression(exp); - } -}; +EXPRESSION_ACTION(dq_string_content, Expression::Type::String) +EXPRESSION_ACTION(sq_string_content, Expression::Type::String) +EXPRESSION_ACTION(key_path, Expression::Type::KeyPath) +EXPRESSION_ACTION(number, Expression::Type::Number) +EXPRESSION_ACTION(argument_index, Expression::Type::Argument) -template<> struct action< sq_string_content > -{ - static void apply( const input & in, ParserState & state ) - { - std::cout << in.string() << std::endl; - - Expression exp; - exp.type = Expression::Type::String; - exp.s = in.string(); - - state.addExpression(exp); - } -}; - -template<> struct action< key_path > -{ - static void apply( const input & in, ParserState & state ) - { - std::cout << in.string() << std::endl; - - Expression exp; - exp.type = Expression::Type::KeyPath; - exp.s = in.string(); - - state.addExpression(std::move(exp)); - } -}; - -template<> struct action< number > -{ - static void apply( const input & in, ParserState & state ) - { - std::cout << in.string() << std::endl; - - Expression exp; - exp.type = Expression::Type::Number; - exp.s = in.string(); - - state.addExpression(std::move(exp)); - } -}; template<> struct action< true_pred > { static void apply( const input & in, ParserState & state ) { std::cout << in.string() << std::endl; - Predicate pred(Predicate::Type::True); - state.current().cpnd.sub_predicates.push_back(std::move(pred)); + state.current().cpnd.sub_predicates.emplace_back(Predicate::Type::True); } }; @@ -260,11 +222,11 @@ template<> struct action< false_pred > static void apply( const input & in, ParserState & state ) { std::cout << in.string() << std::endl; - Predicate pred(Predicate::Type::False); - state.current().cpnd.sub_predicates.push_back(std::move(pred)); + state.current().cpnd.sub_predicates.emplace_back(Predicate::Type::False); } }; + #define OPERATOR_ACTION(rule, oper) \ template<> struct action< rule > { \ static void apply( const input & in, ParserState & state ) { \ @@ -304,7 +266,6 @@ template<> struct action< group_pred > static void apply( const input & in, ParserState & state ) { std::cout << "" << std::endl; - state.predicate_stack.pop_back(); } }; @@ -314,7 +275,6 @@ template<> struct action< not_pre > static void apply( const input & in, ParserState & state ) { std::cout << "" << std::endl; - state.negate_next = true; } }; diff --git a/parser/parser.hpp b/parser/parser.hpp index c53887f5..117ca833 100644 --- a/parser/parser.hpp +++ b/parser/parser.hpp @@ -29,8 +29,10 @@ namespace realm { namespace parser { struct Expression { - enum class Type { Number, String, KeyPath } type; + enum class Type { Number, String, KeyPath, Argument } type; std::string s; + Expression() {} + Expression(Type t, std::string s) : type(t), s(s) {} }; struct Predicate From 195f2a21dd925fbcf797e8fe3ea5b309cc31428c Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Tue, 10 Nov 2015 12:51:21 -0800 Subject: [PATCH 13/66] move query building to a separate file --- parser/parser.cpp | 353 ------------------------------------ parser/parser.hpp | 9 +- parser/query_builder.cpp | 381 +++++++++++++++++++++++++++++++++++++++ parser/query_builder.hpp | 34 ++++ 4 files changed, 418 insertions(+), 359 deletions(-) create mode 100644 parser/query_builder.cpp create mode 100644 parser/query_builder.hpp diff --git a/parser/parser.cpp b/parser/parser.cpp index be632cac..03dbd39d 100644 --- a/parser/parser.cpp +++ b/parser/parser.cpp @@ -24,10 +24,6 @@ #include #include -#include -#include "object_store.hpp" -#include "schema.hpp" - using namespace pegtl; namespace realm { @@ -296,355 +292,6 @@ Predicate parse(const std::string &query) return std::move(out_predicate); } - -// check a precondition and throw an exception if it is not met -// this should be used iff the condition being false indicates a bug in the caller -// of the function checking its preconditions -static void precondition(bool condition, const std::string message) { - if (__builtin_expect(condition, 1)) { - return; - } - throw std::runtime_error(message); -} - -// FIXME: TrueExpression and FalseExpression should be supported by core in some way -struct TrueExpression : realm::Expression { - size_t find_first(size_t start, size_t end) const override - { - if (start != end) - return start; - - return not_found; - } - void set_table() override {} - const Table* get_table() const override { return nullptr; } -}; - -struct FalseExpression : realm::Expression { - size_t find_first(size_t, size_t) const override { return not_found; } - void set_table() override {} - const Table* get_table() const override { return nullptr; } -}; - - -// add a clause for numeric constraints based on operator type -template -void add_numeric_constraint_to_query(Query& query, - PropertyType datatype, - Predicate::Operator operatorType, - A lhs, - B rhs) -{ - switch (operatorType) { - case Predicate::Operator::LessThan: - query.and_query(lhs < rhs); - break; - case Predicate::Operator::LessThanOrEqual: - query.and_query(lhs <= rhs); - break; - case Predicate::Operator::GreaterThan: - query.and_query(lhs > rhs); - break; - case Predicate::Operator::GreaterThanOrEqual: - query.and_query(lhs >= rhs); - break; - case Predicate::Operator::Equal: - query.and_query(lhs == rhs); - break; - case Predicate::Operator::NotEqual: - query.and_query(lhs != rhs); - break; - default: - throw std::runtime_error("Unsupported operator for numeric queries."); - } -} - -template -void add_bool_constraint_to_query(Query &query, Predicate::Operator operatorType, A lhs, B rhs) { - switch (operatorType) { - case Predicate::Operator::Equal: - query.and_query(lhs == rhs); - break; - case Predicate::Operator::NotEqual: - query.and_query(lhs != rhs); - break; - default: - throw std::runtime_error("Unsupported operator for numeric queries."); - } -} - -void add_string_constraint_to_query(Query &query, - Predicate::Operator op, - Columns &&column, - StringData value) { - bool case_sensitive = true; - StringData sd = value; - switch (op) { - case Predicate::Operator::BeginsWith: - query.and_query(column.begins_with(sd, case_sensitive)); - break; - case Predicate::Operator::EndsWith: - query.and_query(column.ends_with(sd, case_sensitive)); - break; - case Predicate::Operator::Contains: - query.and_query(column.contains(sd, case_sensitive)); - break; - case Predicate::Operator::Equal: - query.and_query(column.equal(sd, case_sensitive)); - break; - case Predicate::Operator::NotEqual: - query.and_query(column.not_equal(sd, case_sensitive)); - break; - default: - throw std::runtime_error("Unsupported operator for string queries."); - } -} - -void add_string_constraint_to_query(realm::Query& query, - Predicate::Operator op, - StringData value, - Columns &&column) { - bool case_sensitive = true; - StringData sd = value; - switch (op) { - case Predicate::Operator::Equal: - query.and_query(column.equal(sd, case_sensitive)); - break; - case Predicate::Operator::NotEqual: - query.and_query(column.not_equal(sd, case_sensitive)); - break; - default: - throw std::runtime_error("Substring comparison not supported for keypath substrings."); - } -} - -template -struct ColumnOfTypeHelper { - static Columns convert(TableGetter&& table, unsigned int idx) - { - return table()->template column(idx); - } -}; - -template -struct ColumnOfTypeHelper { - static Columns convert(TableGetter&& table, unsigned int idx) - { - return table()->template column(idx); - } -}; - -template -struct ValueOfTypeHelper; - -template -struct ValueOfTypeHelper { - static Int convert(TableGetter&&, const std::string & value) - { - assert(0); - } -}; - -template -struct ValueOfTypeHelper { - static bool convert(TableGetter&&, const std::string & value) - { - assert(0); - } -}; - -template -struct ValueOfTypeHelper { - static Double convert(TableGetter&&, const std::string & value) - { - return std::stod(value); - } -}; - -template -struct ValueOfTypeHelper { - static Float convert(TableGetter&&, const std::string & value) - { - return std::stof(value); - } -}; - -template -struct ValueOfTypeHelper { - static Int convert(TableGetter&&, const std::string & value) - { - return std::stoll(value); - } -}; - -template -struct ValueOfTypeHelper { - static std::string convert(TableGetter&&, const std::string & value) - { - return value; - } -}; - -template -auto value_of_type_for_query(TableGetter&& tables, Value&& value) -{ - const bool isColumnIndex = std::is_same::type>::value; - using helper = std::conditional_t, - ValueOfTypeHelper>; - return helper::convert(std::forward(tables), std::forward(value)); -} - -std::vector &split(const std::string &s, char delim, std::vector &elems) { - std::stringstream ss(s); - std::string item; - while (std::getline(ss, item, delim)) { - elems.push_back(item); - } - return elems; -} - -std::vector split(const std::string &s, char delim) { - std::vector elems; - split(s, delim, elems); - return elems; -} - -Property *get_property_from_key_path(Schema &schema, ObjectSchema &desc, const std::string &key_path, std::vector &indexes) -{ - Property *prop = nullptr; - - auto paths = split(key_path, '.'); - for (size_t index = 0; index < paths.size(); index++) { - if (prop) { - precondition(prop->type == PropertyTypeObject || prop->type == PropertyTypeArray, - (std::string)"Property '" + paths[index] + "' is not a link in object of type '" + desc.name + "'"); - indexes.push_back(prop->table_column); - - } - prop = desc.property_for_name(paths[index]); - precondition(prop != nullptr, "No property '" + paths[index] + "' on object of type '" + desc.name + "'"); - - if (prop->object_type.size()) { - desc = *schema.find(prop->object_type); - } - } - return prop; -} - -template - void do_add_comparison_to_query(Query &query, Schema &schema, ObjectSchema &object_schema, Property *prop, - Predicate::Operator op, const std::vector& indexes, T... values) -{ - auto table = [&] { - TableRef& tbl = query.get_table(); - for (size_t col : indexes) { - tbl->link(col); // mutates m_link_chain on table - } - return tbl.get(); - }; - - auto type = prop->type; - switch (type) { - case PropertyTypeBool: - add_bool_constraint_to_query(query, op, value_of_type_for_query(table, values)...); - break; - case PropertyTypeDate: - add_numeric_constraint_to_query(query, type, op, value_of_type_for_query(table, values)...); - break; - case PropertyTypeDouble: - add_numeric_constraint_to_query(query, type, op, value_of_type_for_query(table, values)...); - break; - case PropertyTypeFloat: - add_numeric_constraint_to_query(query, type, op, value_of_type_for_query(table, values)...); - break; - case PropertyTypeInt: - add_numeric_constraint_to_query(query, type, op, value_of_type_for_query(table, values)...); - break; - case PropertyTypeString: - case PropertyTypeData: - add_string_constraint_to_query(query, op, value_of_type_for_query(table, values)...); - break; - default: { - throw std::runtime_error((std::string)"Object type " + string_for_property_type(type) + " not supported"); - } - } -} - -void add_comparison_to_query(Query &query, Predicate &pred, Schema &schema, ObjectSchema &object_schema) -{ - std::vector indexes; - Predicate::Comparison &cmpr = pred.cmpr; - auto t0 = cmpr.expr[0].type, t1 = cmpr.expr[1].type; - if (t0 == Expression::Type::KeyPath && t1 != Expression::Type::KeyPath) { - Property *prop = get_property_from_key_path(schema, object_schema, cmpr.expr[0].s, indexes); - do_add_comparison_to_query(query, schema, object_schema, prop, cmpr.op, indexes, prop->table_column, cmpr.expr[1].s); - } - else if (t0 != Expression::Type::KeyPath && t1 == Expression::Type::KeyPath) { - Property *prop = get_property_from_key_path(schema, object_schema, cmpr.expr[1].s, indexes); - do_add_comparison_to_query(query, schema, object_schema, prop, cmpr.op, indexes, cmpr.expr[0].s, prop->table_column); - } - else { - throw std::runtime_error("Predicate expressions must compare a keypath and another keypath or a constant value"); - } -} - -void update_query_with_predicate(Query &query, Predicate &pred, Schema &schema, ObjectSchema &object_schema) -{ - if (pred.negate) { - query.Not(); - } - - switch (pred.type) { - case Predicate::Type::And: - query.group(); - for (auto &sub : pred.cpnd.sub_predicates) { - update_query_with_predicate(query, sub, schema, object_schema); - } - if (!pred.cpnd.sub_predicates.size()) { - query.and_query(new TrueExpression); - } - query.end_group(); - break; - - case Predicate::Type::Or: - query.group(); - for (auto &sub : pred.cpnd.sub_predicates) { - query.Or(); - update_query_with_predicate(query, sub, schema, object_schema); - } - if (!pred.cpnd.sub_predicates.size()) { - query.and_query(new FalseExpression); - } - query.end_group(); - break; - - case Predicate::Type::Comparison: { - add_comparison_to_query(query, pred, schema, object_schema); - break; - } - case Predicate::Type::True: - query.and_query(new TrueExpression); - break; - - case Predicate::Type::False: - query.and_query(new FalseExpression); - break; - - default: - throw std::runtime_error("Invalid predicate type"); - break; - } -} - -void apply_predicate(Query &query, Predicate &predicate, Schema &schema, std::string objectType) { - update_query_with_predicate(query, predicate, schema, *schema.find(objectType)); - - // Test the constructed query in core - std::string validateMessage = query.validate(); - precondition(validateMessage.empty(), validateMessage.c_str()); -} - }} diff --git a/parser/parser.hpp b/parser/parser.hpp index 117ca833..568c446b 100644 --- a/parser/parser.hpp +++ b/parser/parser.hpp @@ -16,14 +16,13 @@ // //////////////////////////////////////////////////////////////////////////// -#ifndef REALM_EXTERNAL_COMMIT_HELPER_HPP -#define REALM_EXTERNAL_COMMIT_HELPER_HPP +#ifndef REALM_PARSER_HPP +#define REALM_PARSER_HPP #include #include namespace realm { - class Query; class Schema; namespace parser { @@ -77,9 +76,7 @@ namespace realm { }; Predicate parse(const std::string &query); - - void apply_predicate(Query &query, Predicate &predicate, Schema &schema, std::string objectType); } } -#endif // REALM_EXTERNAL_COMMIT_HELPER_HPP +#endif // REALM_PARSER_HPP diff --git a/parser/query_builder.cpp b/parser/query_builder.cpp new file mode 100644 index 00000000..004b821e --- /dev/null +++ b/parser/query_builder.cpp @@ -0,0 +1,381 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 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 "query_builder.hpp" +#include "parser.hpp" + +#include +#include "object_store.hpp" +#include "schema.hpp" + +#include + +namespace realm { +namespace query_builder { +using namespace parser; + +// check a precondition and throw an exception if it is not met +// this should be used iff the condition being false indicates a bug in the caller +// of the function checking its preconditions +static void precondition(bool condition, const std::string message) { + if (__builtin_expect(condition, 1)) { + return; + } + throw std::runtime_error(message); +} + +// FIXME: TrueExpression and FalseExpression should be supported by core in some way +struct TrueExpression : realm::Expression { + size_t find_first(size_t start, size_t end) const override + { + if (start != end) + return start; + + return not_found; + } + void set_table() override {} + const Table* get_table() const override { return nullptr; } +}; + +struct FalseExpression : realm::Expression { + size_t find_first(size_t, size_t) const override { return not_found; } + void set_table() override {} + const Table* get_table() const override { return nullptr; } +}; + + +// add a clause for numeric constraints based on operator type +template +void add_numeric_constraint_to_query(Query& query, + PropertyType datatype, + Predicate::Operator operatorType, + A lhs, + B rhs) +{ + switch (operatorType) { + case Predicate::Operator::LessThan: + query.and_query(lhs < rhs); + break; + case Predicate::Operator::LessThanOrEqual: + query.and_query(lhs <= rhs); + break; + case Predicate::Operator::GreaterThan: + query.and_query(lhs > rhs); + break; + case Predicate::Operator::GreaterThanOrEqual: + query.and_query(lhs >= rhs); + break; + case Predicate::Operator::Equal: + query.and_query(lhs == rhs); + break; + case Predicate::Operator::NotEqual: + query.and_query(lhs != rhs); + break; + default: + throw std::runtime_error("Unsupported operator for numeric queries."); + } +} + +template +void add_bool_constraint_to_query(Query &query, Predicate::Operator operatorType, A lhs, B rhs) { + switch (operatorType) { + case Predicate::Operator::Equal: + query.and_query(lhs == rhs); + break; + case Predicate::Operator::NotEqual: + query.and_query(lhs != rhs); + break; + default: + throw std::runtime_error("Unsupported operator for numeric queries."); + } +} + +void add_string_constraint_to_query(Query &query, + Predicate::Operator op, + Columns &&column, + StringData value) { + bool case_sensitive = true; + StringData sd = value; + switch (op) { + case Predicate::Operator::BeginsWith: + query.and_query(column.begins_with(sd, case_sensitive)); + break; + case Predicate::Operator::EndsWith: + query.and_query(column.ends_with(sd, case_sensitive)); + break; + case Predicate::Operator::Contains: + query.and_query(column.contains(sd, case_sensitive)); + break; + case Predicate::Operator::Equal: + query.and_query(column.equal(sd, case_sensitive)); + break; + case Predicate::Operator::NotEqual: + query.and_query(column.not_equal(sd, case_sensitive)); + break; + default: + throw std::runtime_error("Unsupported operator for string queries."); + } +} + +void add_string_constraint_to_query(realm::Query& query, + Predicate::Operator op, + StringData value, + Columns &&column) { + bool case_sensitive = true; + StringData sd = value; + switch (op) { + case Predicate::Operator::Equal: + query.and_query(column.equal(sd, case_sensitive)); + break; + case Predicate::Operator::NotEqual: + query.and_query(column.not_equal(sd, case_sensitive)); + break; + default: + throw std::runtime_error("Substring comparison not supported for keypath substrings."); + } +} + +template +struct ColumnOfTypeHelper { + static Columns convert(TableGetter&& table, unsigned int idx) + { + return table()->template column(idx); + } +}; + +template +struct ColumnOfTypeHelper { + static Columns convert(TableGetter&& table, unsigned int idx) + { + return table()->template column(idx); + } +}; + +template +struct ValueOfTypeHelper; + +template +struct ValueOfTypeHelper { + static Int convert(TableGetter&&, const std::string & value) + { + assert(0); + } +}; + +template +struct ValueOfTypeHelper { + static bool convert(TableGetter&&, const std::string & value) + { + assert(0); + } +}; + +template +struct ValueOfTypeHelper { + static Double convert(TableGetter&&, const std::string & value) + { + return std::stod(value); + } +}; + +template +struct ValueOfTypeHelper { + static Float convert(TableGetter&&, const std::string & value) + { + return std::stof(value); + } +}; + +template +struct ValueOfTypeHelper { + static Int convert(TableGetter&&, const std::string & value) + { + return std::stoll(value); + } +}; + +template +struct ValueOfTypeHelper { + static std::string convert(TableGetter&&, const std::string & value) + { + return value; + } +}; + +template +auto value_of_type_for_query(TableGetter&& tables, Value&& value) +{ + const bool isColumnIndex = std::is_same::type>::value; + using helper = std::conditional_t, + ValueOfTypeHelper>; + return helper::convert(std::forward(tables), std::forward(value)); +} + +std::vector &split(const std::string &s, char delim, std::vector &elems) { + std::stringstream ss(s); + std::string item; + while (std::getline(ss, item, delim)) { + elems.push_back(item); + } + return elems; +} + +std::vector split(const std::string &s, char delim) { + std::vector elems; + split(s, delim, elems); + return elems; +} + +Property *get_property_from_key_path(Schema &schema, ObjectSchema &desc, const std::string &key_path, std::vector &indexes) +{ + Property *prop = nullptr; + + auto paths = split(key_path, '.'); + for (size_t index = 0; index < paths.size(); index++) { + if (prop) { + precondition(prop->type == PropertyTypeObject || prop->type == PropertyTypeArray, + (std::string)"Property '" + paths[index] + "' is not a link in object of type '" + desc.name + "'"); + indexes.push_back(prop->table_column); + + } + prop = desc.property_for_name(paths[index]); + precondition(prop != nullptr, "No property '" + paths[index] + "' on object of type '" + desc.name + "'"); + + if (prop->object_type.size()) { + desc = *schema.find(prop->object_type); + } + } + return prop; +} + +template +void do_add_comparison_to_query(Query &query, Schema &schema, ObjectSchema &object_schema, Property *prop, + Predicate::Operator op, const std::vector& indexes, T... values) +{ + auto table = [&] { + TableRef& tbl = query.get_table(); + for (size_t col : indexes) { + tbl->link(col); // mutates m_link_chain on table + } + return tbl.get(); + }; + + auto type = prop->type; + switch (type) { + case PropertyTypeBool: + add_bool_constraint_to_query(query, op, value_of_type_for_query(table, values)...); + break; + case PropertyTypeDate: + add_numeric_constraint_to_query(query, type, op, value_of_type_for_query(table, values)...); + break; + case PropertyTypeDouble: + add_numeric_constraint_to_query(query, type, op, value_of_type_for_query(table, values)...); + break; + case PropertyTypeFloat: + add_numeric_constraint_to_query(query, type, op, value_of_type_for_query(table, values)...); + break; + case PropertyTypeInt: + add_numeric_constraint_to_query(query, type, op, value_of_type_for_query(table, values)...); + break; + case PropertyTypeString: + case PropertyTypeData: + add_string_constraint_to_query(query, op, value_of_type_for_query(table, values)...); + break; + default: { + throw std::runtime_error((std::string)"Object type " + string_for_property_type(type) + " not supported"); + } + } +} + +void add_comparison_to_query(Query &query, Predicate &pred, Schema &schema, ObjectSchema &object_schema) +{ + std::vector indexes; + Predicate::Comparison &cmpr = pred.cmpr; + auto t0 = cmpr.expr[0].type, t1 = cmpr.expr[1].type; + if (t0 == parser::Expression::Type::KeyPath && t1 != parser::Expression::Type::KeyPath) { + Property *prop = get_property_from_key_path(schema, object_schema, cmpr.expr[0].s, indexes); + do_add_comparison_to_query(query, schema, object_schema, prop, cmpr.op, indexes, prop->table_column, cmpr.expr[1].s); + } + else if (t0 != parser::Expression::Type::KeyPath && t1 == parser::Expression::Type::KeyPath) { + Property *prop = get_property_from_key_path(schema, object_schema, cmpr.expr[1].s, indexes); + do_add_comparison_to_query(query, schema, object_schema, prop, cmpr.op, indexes, cmpr.expr[0].s, prop->table_column); + } + else { + throw std::runtime_error("Predicate expressions must compare a keypath and another keypath or a constant value"); + } +} + +void update_query_with_predicate(Query &query, Predicate &pred, Schema &schema, ObjectSchema &object_schema) +{ + if (pred.negate) { + query.Not(); + } + + switch (pred.type) { + case Predicate::Type::And: + query.group(); + for (auto &sub : pred.cpnd.sub_predicates) { + update_query_with_predicate(query, sub, schema, object_schema); + } + if (!pred.cpnd.sub_predicates.size()) { + query.and_query(new TrueExpression); + } + query.end_group(); + break; + + case Predicate::Type::Or: + query.group(); + for (auto &sub : pred.cpnd.sub_predicates) { + query.Or(); + update_query_with_predicate(query, sub, schema, object_schema); + } + if (!pred.cpnd.sub_predicates.size()) { + query.and_query(new FalseExpression); + } + query.end_group(); + break; + + case Predicate::Type::Comparison: { + add_comparison_to_query(query, pred, schema, object_schema); + break; + } + case Predicate::Type::True: + query.and_query(new TrueExpression); + break; + + case Predicate::Type::False: + query.and_query(new FalseExpression); + break; + + default: + throw std::runtime_error("Invalid predicate type"); + break; + } +} + +void apply_predicate(Query &query, Predicate &predicate, Schema &schema, std::string objectType) +{ + update_query_with_predicate(query, predicate, schema, *schema.find(objectType)); + + // Test the constructed query in core + std::string validateMessage = query.validate(); + precondition(validateMessage.empty(), validateMessage.c_str()); +} + +}} diff --git a/parser/query_builder.hpp b/parser/query_builder.hpp new file mode 100644 index 00000000..8a408ec8 --- /dev/null +++ b/parser/query_builder.hpp @@ -0,0 +1,34 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 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. +// +//////////////////////////////////////////////////////////////////////////// + +#ifndef REALM_QUERY_BUILDER_HPP +#define REALM_QUERY_BUILDER_HPP + +#include +#include "parser.hpp" + +namespace realm { + class Query; + class Schema; + + namespace query_builder { + void apply_predicate(Query &query, parser::Predicate &predicate, Schema &schema, std::string objectType); + } +} + +#endif // REALM_QUERY_BUILDER_HPP From e078b22c9aa8292b0c112752a54521a660d71d6f Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Tue, 10 Nov 2015 13:17:27 -0800 Subject: [PATCH 14/66] add required padding around string operators, use pegtl_istring_t --- parser/parser.cpp | 30 ++++++++++++++++++------------ parser/query.abnf | 23 ----------------------- 2 files changed, 18 insertions(+), 35 deletions(-) delete mode 100644 parser/query.abnf diff --git a/parser/parser.cpp b/parser/parser.cpp index 03dbd39d..699739db 100644 --- a/parser/parser.cpp +++ b/parser/parser.cpp @@ -23,6 +23,7 @@ #include #include #include +#include using namespace pegtl; @@ -71,27 +72,32 @@ struct lteq : pegtl::string< '<', '=' > {}; struct lt : one< '<' > {}; struct gteq : pegtl::string< '>', '=' > {}; struct gt : one< '>' > {}; -struct begins : istring< 'b','e','g','i','n','s','w','i','t','h' > {}; -struct ends : istring< 'e','n','d','s','w','i','t','h' > {}; -struct contains : istring< 'c','o','n','t','a','i','n','s' > {}; -struct oper : sor< eq, noteq, lteq, lt, gteq, gt, begins, ends, contains > {}; +struct contains : pegtl_istring_t("contains") {}; +struct begins : pegtl_istring_t("beginswith") {}; +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 symbolic_oper : pad< sor< eq, noteq, lteq, lt, gteq, gt >, blank > {}; // predicates -struct comparison_pred : seq< expr, pad< oper, blank >, expr > {}; +struct comparison_pred : seq< expr, sor< padded_oper, symbolic_oper >, expr > {}; struct pred; struct group_pred : if_must< one< '(' >, pad< pred, blank >, one< ')' > > {}; -struct true_pred : sor< istring<'t','r','u','e','p','r','e','d','i','c','a','t','e'>, istring<'t','r','u','e'> > {}; -struct false_pred : sor< istring<'f','a','l','s','e','p','r','e','d','i','c','a','t','e'>, istring<'f','a','l','s','e'> > {}; +struct true_pred : sor< pegtl_istring_t("truepredicate"), pegtl_istring_t("true") > {}; +struct false_pred : sor< pegtl_istring_t("falsepredicate"), pegtl_istring_t("false") > {}; -struct not_pre : sor< seq< one< '!' >, star< blank > >, seq< istring< 'N', 'O', 'T' >, plus< blank > > > {}; +struct not_pre : sor< seq< one< '!' >, star< blank > >, seq< pegtl_istring_t("not"), plus< blank > > > {}; struct atom_pred : seq< opt< not_pre >, pad< sor< group_pred, true_pred, false_pred, comparison_pred >, blank > > {}; -struct and_op : sor< two< '&' >, istring< 'A', 'N', 'D' > > {}; -struct or_op : sor< two< '|' >, istring< 'O', 'R' > > {}; +struct and_op : sor< pad< two< '&' >, blank >, pad_plus< pegtl_istring_t("and"), blank > > {}; +struct or_op : sor< pad< two< '|' >, blank> , pad_plus< pegtl_istring_t("or"), blank > > {}; -struct or_ext : seq< pad< or_op, blank >, pred > {}; -struct and_ext : seq< pad< and_op, blank >, pred > {}; +struct or_ext : if_must< or_op, pred > {}; +struct and_ext : if_must< and_op, pred > {}; struct and_pred : seq< atom_pred, star< and_ext > > {}; struct pred : seq< and_pred, star< or_ext > > {}; diff --git a/parser/query.abnf b/parser/query.abnf deleted file mode 100644 index d315fd35..00000000 --- a/parser/query.abnf +++ /dev/null @@ -1,23 +0,0 @@ - - -pred = expr 1*WSP oper 1*WSP expr -;pred =/ "(" *WSP pred *WSP ")" -;pred =/ ("NOT" / "!") 1*WSP pred -;pred =/ pred 1*WSP ("OR" / "||") !*WSP pred -;pred =/ pred 1*WSP ("AND" / "&&") 1*WSP pred - -oper = "=" / "==" / "!=" / "<" / "<=" / ">" / ">=" - -expr = string / num / key-path - -key-path = key *("." key) -key = 1*(ALPHA / "_") - -string = dq-string / sq-string -sq-string = "'" *(%x20-ffffffff) "'" -dq-string = DQUOTE *("\\" / %x20-21 / %x23-ffffffff) DQUOTE - -num = ["-"] (float-num / int-num / hex-num) -float-num = (*DIGIT "." 1*DIGIT / "." 1*DIGIT) -int-num = 1*DIGIT -hex-num = ("0x" / "0X") 1*HEXDIG From b97728ba3350f00341e8e6c252716a377b3c56fd Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Tue, 10 Nov 2015 14:24:30 -0800 Subject: [PATCH 15/66] support for querying boolean properties --- parser/parser.cpp | 14 ++++++++------ parser/parser.hpp | 2 +- parser/query_builder.cpp | 34 +++++++++++++++++----------------- 3 files changed, 26 insertions(+), 24 deletions(-) diff --git a/parser/parser.cpp b/parser/parser.cpp index 699739db..2479b287 100644 --- a/parser/parser.cpp +++ b/parser/parser.cpp @@ -23,7 +23,6 @@ #include #include #include -#include using namespace pegtl; @@ -56,6 +55,9 @@ struct int_num : plus< digit > {}; 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") {}; + // key paths struct key_path : list< seq< sor< alpha, one< '_' > >, star< sor< alnum, one< '_', '-' > > > >, one< '.' > > {}; @@ -64,7 +66,7 @@ struct argument_index : until< at< one< '}' > >, must< digit > > {}; struct argument : seq< one< '{' >, must< argument_index >, any > {}; // expressions and operators -struct expr : sor< dq_string, sq_string, key_path, number, argument > {}; +struct expr : sor< dq_string, sq_string, number, argument, true_value, false_value, key_path > {}; struct eq : sor< two< '=' >, one< '=' > > {}; struct noteq : pegtl::string< '!', '=' > {}; @@ -87,8 +89,8 @@ struct comparison_pred : seq< expr, sor< padded_oper, symbolic_oper >, expr > {} struct pred; struct group_pred : if_must< one< '(' >, pad< pred, blank >, one< ')' > > {}; -struct true_pred : sor< pegtl_istring_t("truepredicate"), pegtl_istring_t("true") > {}; -struct false_pred : sor< pegtl_istring_t("falsepredicate"), pegtl_istring_t("false") > {}; +struct true_pred : pegtl_istring_t("truepredicate") {}; +struct false_pred : pegtl_istring_t("falsepredicate") {}; struct not_pre : sor< seq< one< '!' >, star< blank > >, seq< pegtl_istring_t("not"), plus< blank > > > {}; struct atom_pred : seq< opt< not_pre >, pad< sor< group_pred, true_pred, false_pred, comparison_pred >, blank > > {}; @@ -207,6 +209,8 @@ EXPRESSION_ACTION(dq_string_content, Expression::Type::String) EXPRESSION_ACTION(sq_string_content, Expression::Type::String) 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(argument_index, Expression::Type::Argument) @@ -228,7 +232,6 @@ template<> struct action< false_pred > } }; - #define OPERATOR_ACTION(rule, oper) \ template<> struct action< rule > { \ static void apply( const input & in, ParserState & state ) { \ @@ -245,7 +248,6 @@ OPERATOR_ACTION(begins, Predicate::Operator::BeginsWith) OPERATOR_ACTION(ends, Predicate::Operator::EndsWith) OPERATOR_ACTION(contains, Predicate::Operator::Contains) - template<> struct action< one< '(' > > { static void apply( const input & in, ParserState & state ) diff --git a/parser/parser.hpp b/parser/parser.hpp index 568c446b..4792285a 100644 --- a/parser/parser.hpp +++ b/parser/parser.hpp @@ -28,7 +28,7 @@ namespace realm { namespace parser { struct Expression { - enum class Type { Number, String, KeyPath, Argument } type; + enum class Type { Number, String, KeyPath, Argument, True, False } type; std::string s; Expression() {} Expression(Type t, std::string s) : type(t), s(s) {} diff --git a/parser/query_builder.cpp b/parser/query_builder.cpp index 004b821e..8f4aeeee 100644 --- a/parser/query_builder.cpp +++ b/parser/query_builder.cpp @@ -152,7 +152,7 @@ void add_string_constraint_to_query(realm::Query& query, template struct ColumnOfTypeHelper { - static Columns convert(TableGetter&& table, unsigned int idx) + static Columns convert(TableGetter&& table, size_t idx) { return table()->template column(idx); } @@ -160,7 +160,7 @@ struct ColumnOfTypeHelper { template struct ColumnOfTypeHelper { - static Columns convert(TableGetter&& table, unsigned int idx) + static Columns convert(TableGetter&& table, size_t idx) { return table()->template column(idx); } @@ -171,7 +171,7 @@ struct ValueOfTypeHelper; template struct ValueOfTypeHelper { - static Int convert(TableGetter&&, const std::string & value) + static Int convert(TableGetter&&, const parser::Expression & value) { assert(0); } @@ -179,41 +179,41 @@ struct ValueOfTypeHelper { template struct ValueOfTypeHelper { - static bool convert(TableGetter&&, const std::string & value) + static bool convert(TableGetter&&, const parser::Expression & value) { - assert(0); + return value.type == parser::Expression::Type::True; } }; template struct ValueOfTypeHelper { - static Double convert(TableGetter&&, const std::string & value) + static Double convert(TableGetter&&, const parser::Expression & value) { - return std::stod(value); + return std::stod(value.s); } }; template struct ValueOfTypeHelper { - static Float convert(TableGetter&&, const std::string & value) + static Float convert(TableGetter&&, const parser::Expression & value) { - return std::stof(value); + return std::stof(value.s); } }; template struct ValueOfTypeHelper { - static Int convert(TableGetter&&, const std::string & value) + static Int convert(TableGetter&&, const parser::Expression & value) { - return std::stoll(value); + return std::stoll(value.s); } }; template struct ValueOfTypeHelper { - static std::string convert(TableGetter&&, const std::string & value) + static std::string convert(TableGetter&&, const parser::Expression & value) { - return value; + return value.s; } }; @@ -222,8 +222,8 @@ auto value_of_type_for_query(TableGetter&& tables, Value&& value) { const bool isColumnIndex = std::is_same::type>::value; using helper = std::conditional_t, - ValueOfTypeHelper>; + ColumnOfTypeHelper, + ValueOfTypeHelper>; return helper::convert(std::forward(tables), std::forward(value)); } @@ -310,11 +310,11 @@ void add_comparison_to_query(Query &query, Predicate &pred, Schema &schema, Obje auto t0 = cmpr.expr[0].type, t1 = cmpr.expr[1].type; if (t0 == parser::Expression::Type::KeyPath && t1 != parser::Expression::Type::KeyPath) { Property *prop = get_property_from_key_path(schema, object_schema, cmpr.expr[0].s, indexes); - do_add_comparison_to_query(query, schema, object_schema, prop, cmpr.op, indexes, prop->table_column, cmpr.expr[1].s); + do_add_comparison_to_query(query, schema, object_schema, prop, cmpr.op, indexes, prop->table_column, cmpr.expr[1]); } else if (t0 != parser::Expression::Type::KeyPath && t1 == parser::Expression::Type::KeyPath) { Property *prop = get_property_from_key_path(schema, object_schema, cmpr.expr[1].s, indexes); - do_add_comparison_to_query(query, schema, object_schema, prop, cmpr.op, indexes, cmpr.expr[0].s, prop->table_column); + do_add_comparison_to_query(query, schema, object_schema, prop, cmpr.op, indexes, cmpr.expr[0], prop->table_column); } else { throw std::runtime_error("Predicate expressions must compare a keypath and another keypath or a constant value"); From 260ca1884526ed5bdee19f928f1b6be9f696e8ab Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Tue, 10 Nov 2015 15:58:04 -0800 Subject: [PATCH 16/66] support query format strings --- object_accessor.hpp | 3 + object_store.hpp | 2 +- parser/query_builder.cpp | 214 +++++++++++++++++++++------------------ parser/query_builder.hpp | 46 ++++++++- 4 files changed, 167 insertions(+), 98 deletions(-) diff --git a/object_accessor.hpp b/object_accessor.hpp index 5cc7a358..dfae7c16 100644 --- a/object_accessor.hpp +++ b/object_accessor.hpp @@ -76,6 +76,9 @@ namespace realm { static size_t to_object_index(ContextType ctx, SharedRealm realm, ValueType &val, const std::string &type, bool try_update); static ValueType from_object(ContextType ctx, Object); + // object index for an existing object + static size_t to_existing_object_index(ContextType ctx, ValueType &val); + // list value acessors static size_t list_size(ContextType ctx, ValueType &val); static ValueType list_value_at_index(ContextType ctx, ValueType &val, size_t index); diff --git a/object_store.hpp b/object_store.hpp index 60671f55..e038604c 100644 --- a/object_store.hpp +++ b/object_store.hpp @@ -19,6 +19,7 @@ #ifndef REALM_OBJECT_STORE_HPP #define REALM_OBJECT_STORE_HPP +#include "schema.hpp" #include "object_schema.hpp" #include "property.hpp" @@ -29,7 +30,6 @@ namespace realm { class ObjectSchemaValidationException; - class Schema; class ObjectStore { public: diff --git a/parser/query_builder.cpp b/parser/query_builder.cpp index 8f4aeeee..b924eaa3 100644 --- a/parser/query_builder.cpp +++ b/parser/query_builder.cpp @@ -62,7 +62,6 @@ struct FalseExpression : realm::Expression { // add a clause for numeric constraints based on operator type template void add_numeric_constraint_to_query(Query& query, - PropertyType datatype, Predicate::Operator operatorType, A lhs, B rhs) @@ -150,152 +149,176 @@ void add_string_constraint_to_query(realm::Query& query, } } -template -struct ColumnOfTypeHelper { - static Columns convert(TableGetter&& table, size_t idx) + +using KeyPath = std::vector; +KeyPath key_path_from_string(const std::string &s) { + std::stringstream ss(s); + std::string item; + KeyPath key_path; + while (std::getline(ss, item, '.')) { + key_path.push_back(item); + } + return key_path; +} + +struct PropertyExpression +{ + Property *prop = nullptr; + std::vector indexes; + std::function table_getter; + + PropertyExpression(Query &query, Schema &schema, ObjectSchema &desc, const std::string &key_path_string) { - return table()->template column(idx); + KeyPath key_path = key_path_from_string(key_path_string); + for (size_t index = 0; index < key_path.size(); index++) { + if (prop) { + precondition(prop->type == PropertyTypeObject || prop->type == PropertyTypeArray, + (std::string)"Property '" + key_path[index] + "' is not a link in object of type '" + desc.name + "'"); + indexes.push_back(prop->table_column); + + } + prop = desc.property_for_name(key_path[index]); + precondition(prop != nullptr, "No property '" + key_path[index] + "' on object of type '" + desc.name + "'"); + + if (prop->object_type.size()) { + desc = *schema.find(prop->object_type); + } + } + + table_getter = [&] { + TableRef& tbl = query.get_table(); + for (size_t col : indexes) { + tbl->link(col); // mutates m_link_chain on table + } + return tbl.get(); + }; + } +}; + +template +struct ColumnGetter { + static Columns convert(TableGetter&& table, const PropertyExpression & expr, Arguments &args) + { + return table()->template column(expr.prop->table_column); } }; template -struct ColumnOfTypeHelper { - static Columns convert(TableGetter&& table, size_t idx) +struct ColumnGetter { + static Columns convert(TableGetter&& table, const PropertyExpression & expr, Arguments &args) { - return table()->template column(idx); + return table()->template column(expr.prop->table_column); } }; template -struct ValueOfTypeHelper; +struct ValueGetter; template -struct ValueOfTypeHelper { - static Int convert(TableGetter&&, const parser::Expression & value) +struct ValueGetter { + static Int convert(TableGetter&&, const parser::Expression & value, Arguments &args) { - assert(0); + if (value.type != parser::Expression::Type::Argument) { + throw std::runtime_error("You must pass in a date argument to compare"); + } + DateTime dt = args.datetime_for_argument(std::stoi(value.s)); + return dt.get_datetime(); } }; template -struct ValueOfTypeHelper { - static bool convert(TableGetter&&, const parser::Expression & value) +struct ValueGetter { + static bool convert(TableGetter&&, const parser::Expression & value, Arguments &args) { + if (value.type == parser::Expression::Type::Argument) { + return args.bool_for_argument(std::stoi(value.s)); + } return value.type == parser::Expression::Type::True; } }; template -struct ValueOfTypeHelper { - static Double convert(TableGetter&&, const parser::Expression & value) +struct ValueGetter { + static Double convert(TableGetter&&, const parser::Expression & value, Arguments &args) { + if (value.type == parser::Expression::Type::Argument) { + return args.double_for_argument(std::stoi(value.s)); + } return std::stod(value.s); } }; template -struct ValueOfTypeHelper { - static Float convert(TableGetter&&, const parser::Expression & value) +struct ValueGetter { + static Float convert(TableGetter&&, const parser::Expression & value, Arguments &args) { + if (value.type == parser::Expression::Type::Argument) { + return args.float_for_argument(std::stoi(value.s)); + } return std::stof(value.s); } }; template -struct ValueOfTypeHelper { - static Int convert(TableGetter&&, const parser::Expression & value) +struct ValueGetter { + static Int convert(TableGetter&&, const parser::Expression & value, Arguments &args) { + if (value.type == parser::Expression::Type::Argument) { + return args.long_for_argument(std::stoi(value.s)); + } return std::stoll(value.s); } }; template -struct ValueOfTypeHelper { - static std::string convert(TableGetter&&, const parser::Expression & value) +struct ValueGetter { + static std::string convert(TableGetter&&, const parser::Expression & value, Arguments &args) { + if (value.type == parser::Expression::Type::Argument) { + return args.string_for_argument(std::stoi(value.s)); + } return value.s; } }; -template -auto value_of_type_for_query(TableGetter&& tables, Value&& value) +template +auto value_of_type_for_query(TableGetter&& tables, Value&& value, Arguments &args) { - const bool isColumnIndex = std::is_same::type>::value; - using helper = std::conditional_t, - ValueOfTypeHelper>; - return helper::convert(std::forward(tables), std::forward(value)); + const bool isColumn = std::is_same::type>::value; + using helper = std::conditional_t, ValueGetter>; + return helper::convert(std::forward(tables), std::forward(value), args); } -std::vector &split(const std::string &s, char delim, std::vector &elems) { - std::stringstream ss(s); - std::string item; - while (std::getline(ss, item, delim)) { - elems.push_back(item); - } - return elems; -} - -std::vector split(const std::string &s, char delim) { - std::vector elems; - split(s, delim, elems); - return elems; -} - -Property *get_property_from_key_path(Schema &schema, ObjectSchema &desc, const std::string &key_path, std::vector &indexes) +template +void do_add_comparison_to_query(Query &query, Schema &schema, ObjectSchema &object_schema, Predicate::Operator op, + PropertyExpression &expr, A &lhs, B &rhs, Arguments &args) { - Property *prop = nullptr; - - auto paths = split(key_path, '.'); - for (size_t index = 0; index < paths.size(); index++) { - if (prop) { - precondition(prop->type == PropertyTypeObject || prop->type == PropertyTypeArray, - (std::string)"Property '" + paths[index] + "' is not a link in object of type '" + desc.name + "'"); - indexes.push_back(prop->table_column); - - } - prop = desc.property_for_name(paths[index]); - precondition(prop != nullptr, "No property '" + paths[index] + "' on object of type '" + desc.name + "'"); - - if (prop->object_type.size()) { - desc = *schema.find(prop->object_type); - } - } - return prop; -} - -template -void do_add_comparison_to_query(Query &query, Schema &schema, ObjectSchema &object_schema, Property *prop, - Predicate::Operator op, const std::vector& indexes, T... values) -{ - auto table = [&] { - TableRef& tbl = query.get_table(); - for (size_t col : indexes) { - tbl->link(col); // mutates m_link_chain on table - } - return tbl.get(); - }; - - auto type = prop->type; + auto type = expr.prop->type; switch (type) { case PropertyTypeBool: - add_bool_constraint_to_query(query, op, value_of_type_for_query(table, values)...); + 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)); break; case PropertyTypeDate: - add_numeric_constraint_to_query(query, type, op, value_of_type_for_query(table, values)...); + 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)); break; case PropertyTypeDouble: - add_numeric_constraint_to_query(query, type, op, value_of_type_for_query(table, values)...); + 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)); break; case PropertyTypeFloat: - add_numeric_constraint_to_query(query, type, op, value_of_type_for_query(table, values)...); + 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)); break; case PropertyTypeInt: - add_numeric_constraint_to_query(query, type, op, value_of_type_for_query(table, values)...); + 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)); break; case PropertyTypeString: case PropertyTypeData: - add_string_constraint_to_query(query, op, value_of_type_for_query(table, values)...); + 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)); break; default: { throw std::runtime_error((std::string)"Object type " + string_for_property_type(type) + " not supported"); @@ -303,25 +326,24 @@ void do_add_comparison_to_query(Query &query, Schema &schema, ObjectSchema &obje } } -void add_comparison_to_query(Query &query, Predicate &pred, Schema &schema, ObjectSchema &object_schema) +void add_comparison_to_query(Query &query, Predicate &pred, Arguments &args, Schema &schema, ObjectSchema &object_schema) { - std::vector indexes; Predicate::Comparison &cmpr = pred.cmpr; auto t0 = cmpr.expr[0].type, t1 = cmpr.expr[1].type; if (t0 == parser::Expression::Type::KeyPath && t1 != parser::Expression::Type::KeyPath) { - Property *prop = get_property_from_key_path(schema, object_schema, cmpr.expr[0].s, indexes); - do_add_comparison_to_query(query, schema, object_schema, prop, cmpr.op, indexes, prop->table_column, cmpr.expr[1]); + 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); } else if (t0 != parser::Expression::Type::KeyPath && t1 == parser::Expression::Type::KeyPath) { - Property *prop = get_property_from_key_path(schema, object_schema, cmpr.expr[1].s, indexes); - do_add_comparison_to_query(query, schema, object_schema, prop, cmpr.op, indexes, cmpr.expr[0], prop->table_column); + 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); } else { throw std::runtime_error("Predicate expressions must compare a keypath and another keypath or a constant value"); } } -void update_query_with_predicate(Query &query, Predicate &pred, Schema &schema, ObjectSchema &object_schema) +void update_query_with_predicate(Query &query, Predicate &pred, Arguments &arguments, Schema &schema, ObjectSchema &object_schema) { if (pred.negate) { query.Not(); @@ -331,7 +353,7 @@ void update_query_with_predicate(Query &query, Predicate &pred, Schema &schema, case Predicate::Type::And: query.group(); for (auto &sub : pred.cpnd.sub_predicates) { - update_query_with_predicate(query, sub, schema, object_schema); + update_query_with_predicate(query, sub, arguments, schema, object_schema); } if (!pred.cpnd.sub_predicates.size()) { query.and_query(new TrueExpression); @@ -343,7 +365,7 @@ void update_query_with_predicate(Query &query, Predicate &pred, Schema &schema, query.group(); for (auto &sub : pred.cpnd.sub_predicates) { query.Or(); - update_query_with_predicate(query, sub, schema, object_schema); + update_query_with_predicate(query, sub, arguments, schema, object_schema); } if (!pred.cpnd.sub_predicates.size()) { query.and_query(new FalseExpression); @@ -352,7 +374,7 @@ void update_query_with_predicate(Query &query, Predicate &pred, Schema &schema, break; case Predicate::Type::Comparison: { - add_comparison_to_query(query, pred, schema, object_schema); + add_comparison_to_query(query, pred, arguments, schema, object_schema); break; } case Predicate::Type::True: @@ -369,9 +391,9 @@ void update_query_with_predicate(Query &query, Predicate &pred, Schema &schema, } } -void apply_predicate(Query &query, Predicate &predicate, Schema &schema, std::string objectType) +void apply_predicate(Query &query, Predicate &predicate, Arguments &arguments, Schema &schema, std::string objectType) { - update_query_with_predicate(query, predicate, schema, *schema.find(objectType)); + update_query_with_predicate(query, predicate, arguments, schema, *schema.find(objectType)); // Test the constructed query in core std::string validateMessage = query.validate(); diff --git a/parser/query_builder.hpp b/parser/query_builder.hpp index 8a408ec8..1de648b3 100644 --- a/parser/query_builder.hpp +++ b/parser/query_builder.hpp @@ -21,13 +21,57 @@ #include #include "parser.hpp" +#include "object_accessor.hpp" namespace realm { class Query; class Schema; namespace query_builder { - void apply_predicate(Query &query, parser::Predicate &predicate, Schema &schema, std::string objectType); + class Arguments; + + void apply_predicate(Query &query, parser::Predicate &predicate, Arguments &arguments, Schema &schema, std::string objectType); + + class Arguments + { + public: + virtual bool bool_for_argument(size_t argument_index) = 0; + virtual long long long_for_argument(size_t argument_index) = 0; + virtual float float_for_argument(size_t argument_index) = 0; + virtual double double_for_argument(size_t argument_index) = 0; + virtual std::string string_for_argument(size_t argument_index) = 0; + virtual DateTime datetime_for_argument(size_t argument_index) = 0; + virtual size_t object_index_for_argument(size_t argument_index) = 0; + virtual bool is_argument_null(size_t argument_index) = 0; + }; + + template + class ArgumentConverter : public Arguments + { + public: + ArgumentConverter(ContextType context, std::vector arguments) : m_arguments(arguments), m_ctx(context) {}; + + using Accessor = realm::NativeAccessor; + virtual bool bool_for_argument(size_t argument_index) { return Accessor::to_bool(m_ctx, argument_at(argument_index)); } + virtual long long long_for_argument(size_t argument_index) { return Accessor::to_long(m_ctx, argument_at(argument_index)); } + virtual float float_for_argument(size_t argument_index) { return Accessor::to_float(m_ctx, argument_at(argument_index)); } + virtual double double_for_argument(size_t argument_index) { return Accessor::to_double(m_ctx, argument_at(argument_index)); } + virtual std::string string_for_argument(size_t argument_index) { return Accessor::to_string(m_ctx, argument_at(argument_index)); } + virtual DateTime datetime_for_argument(size_t argument_index) { return Accessor::to_datetime(m_ctx, argument_at(argument_index)); } + virtual size_t object_index_for_argument(size_t argument_index) { return Accessor::to_existing_object_index(m_ctx, argument_at(argument_index)); } + virtual bool is_argument_null(size_t argument_index) { return Accessor::is_null(m_ctx, argument_at(argument_index)); } + + private: + std::vector m_arguments; + ContextType m_ctx; + + ValueType &argument_at(size_t index) { + if (index >= m_arguments.size()) { + throw std::out_of_range((std::string)"Argument index " + std::to_string(index) + " out of range 0.." + std::to_string(m_arguments.size()-1)); + } + return m_arguments[index]; + } + }; } } From 4d7f607f49defdaa74300798a1fb60d69144262a Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Tue, 10 Nov 2015 16:11:57 -0800 Subject: [PATCH 17/66] test and fix for date queries --- parser/query_builder.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/parser/query_builder.cpp b/parser/query_builder.cpp index b924eaa3..f1bbb9d4 100644 --- a/parser/query_builder.cpp +++ b/parser/query_builder.cpp @@ -203,14 +203,6 @@ struct ColumnGetter { } }; -template -struct ColumnGetter { - static Columns convert(TableGetter&& table, const PropertyExpression & expr, Arguments &args) - { - return table()->template column(expr.prop->table_column); - } -}; - template struct ValueGetter; From ed1b3c4ecfd4f3d504155a4716de8db69303175c Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Wed, 11 Nov 2015 13:57:05 -0800 Subject: [PATCH 18/66] make precondition a macro --- parser/query_builder.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/parser/query_builder.cpp b/parser/query_builder.cpp index f1bbb9d4..5afdfcb9 100644 --- a/parser/query_builder.cpp +++ b/parser/query_builder.cpp @@ -32,12 +32,7 @@ using namespace parser; // check a precondition and throw an exception if it is not met // this should be used iff the condition being false indicates a bug in the caller // of the function checking its preconditions -static void precondition(bool condition, const std::string message) { - if (__builtin_expect(condition, 1)) { - return; - } - throw std::runtime_error(message); -} +#define precondition(condition, message) if (!__builtin_expect(condition, 1)) { throw std::runtime_error(message); } // FIXME: TrueExpression and FalseExpression should be supported by core in some way struct TrueExpression : realm::Expression { From bd04f5584b6974ac5adb7e0d74b6cb94d1d7bb9b Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Wed, 11 Nov 2015 14:08:59 -0800 Subject: [PATCH 19/66] add macro to enable/disable debug token printing --- parser/parser.cpp | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/parser/parser.cpp b/parser/parser.cpp index 2479b287..c7d18a7f 100644 --- a/parser/parser.cpp +++ b/parser/parser.cpp @@ -137,11 +137,18 @@ struct ParserState template< typename Rule > struct action : nothing< Rule > {}; +#define REALM_PARSER_PRINT_TOKENS +#ifdef REALM_PARSER_PRINT_TOKENS + #define DEBUG_PRINT_TOKEN(string) std::cout << string << std::endl +#else + #define DEBUG_PRINT_TOKEN(string) +#endif + template<> struct action< and_ext > { static void apply( const input & in, ParserState & state ) { - std::cout << "" << in.string() << std::endl; + DEBUG_PRINT_TOKEN(""); // if we were put into an OR group we need to rearrange auto ¤t = state.current(); @@ -171,7 +178,7 @@ template<> struct action< or_ext > { static void apply( const input & in, ParserState & state ) { - std::cout << "" << in.string() << std::endl; + DEBUG_PRINT_TOKEN(""); // if already an OR group do nothing auto ¤t = state.current(); @@ -202,7 +209,7 @@ template<> struct action< or_ext > #define EXPRESSION_ACTION(rule, type) \ template<> struct action< rule > { \ static void apply( const input & in, ParserState & state ) { \ - std::cout << in.string() << std::endl; \ + DEBUG_PRINT_TOKEN(in.string()); \ state.addExpression(Expression(type, in.string())); }}; EXPRESSION_ACTION(dq_string_content, Expression::Type::String) @@ -218,7 +225,7 @@ template<> struct action< true_pred > { static void apply( const input & in, ParserState & state ) { - std::cout << in.string() << std::endl; + DEBUG_PRINT_TOKEN(in.string()); state.current().cpnd.sub_predicates.emplace_back(Predicate::Type::True); } }; @@ -227,7 +234,7 @@ template<> struct action< false_pred > { static void apply( const input & in, ParserState & state ) { - std::cout << in.string() << std::endl; + DEBUG_PRINT_TOKEN(in.string()); state.current().cpnd.sub_predicates.emplace_back(Predicate::Type::False); } }; @@ -235,7 +242,7 @@ template<> struct action< false_pred > #define OPERATOR_ACTION(rule, oper) \ template<> struct action< rule > { \ static void apply( const input & in, ParserState & state ) { \ - std::cout << in.string() << std::endl; \ + DEBUG_PRINT_TOKEN(in.string()); \ state.current().cmpr.op = oper; }}; OPERATOR_ACTION(eq, Predicate::Operator::Equal) @@ -252,7 +259,7 @@ template<> struct action< one< '(' > > { static void apply( const input & in, ParserState & state ) { - std::cout << "" << std::endl; + DEBUG_PRINT_TOKEN(""); Predicate group(Predicate::Type::And); if (state.negate_next) { @@ -269,7 +276,7 @@ template<> struct action< group_pred > { static void apply( const input & in, ParserState & state ) { - std::cout << "" << std::endl; + DEBUG_PRINT_TOKEN(""); state.predicate_stack.pop_back(); } }; @@ -278,7 +285,7 @@ template<> struct action< not_pre > { static void apply( const input & in, ParserState & state ) { - std::cout << "" << std::endl; + DEBUG_PRINT_TOKEN(""); state.negate_next = true; } }; From d455aaf4024d2a343d239f9f1c7df8aae8eba724 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Wed, 11 Nov 2015 14:54:05 -0800 Subject: [PATCH 20/66] add basic test harness for grammer validation --- parser/main.cpp | 7 ------- parser/parser.cpp | 1 - parser/test.cpp | 45 +++++++++++++++++++++++++++++++++++++++++++++ parser/test.sh | 3 +++ 4 files changed, 48 insertions(+), 8 deletions(-) delete mode 100644 parser/main.cpp create mode 100644 parser/test.cpp create mode 100755 parser/test.sh diff --git a/parser/main.cpp b/parser/main.cpp deleted file mode 100644 index def876c3..00000000 --- a/parser/main.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "parser.hpp" - -int main( int argc, char ** argv ) -{ - realm::parser::parse(argv[1]); -} - diff --git a/parser/parser.cpp b/parser/parser.cpp index c7d18a7f..ef15fd48 100644 --- a/parser/parser.cpp +++ b/parser/parser.cpp @@ -137,7 +137,6 @@ struct ParserState template< typename Rule > struct action : nothing< Rule > {}; -#define REALM_PARSER_PRINT_TOKENS #ifdef REALM_PARSER_PRINT_TOKENS #define DEBUG_PRINT_TOKEN(string) std::cout << string << std::endl #else diff --git a/parser/test.cpp b/parser/test.cpp new file mode 100644 index 00000000..23494306 --- /dev/null +++ b/parser/test.cpp @@ -0,0 +1,45 @@ + +#include "parser.hpp" + +#include +#include +#include +#include + +static std::vector valid_queries = { + "truepredicate", + "falsepredicate", + "TRUEPREDICATE", + "FALSEPREDICATE", + "truepredicate && truepredicate" +}; + +static std::vector invalid_queries = { + "predicate", + "truepredicate &&", + "truepredicate & truepredicate", +}; + +int main( int argc, char ** argv ) +{ + for (auto &query : valid_queries) { + std::cout << "valid query: " << query << std::endl; + try { + realm::parser::parse(query); + } catch (std::exception &ex) { + std::cout << "FAILURE - " << ex.what() << std::endl; + } + } + + for (auto &query : invalid_queries) { + std::cout << "invalid query: " << query << std::endl; + try { + realm::parser::parse(query); + } catch (std::exception &ex) { + // std::cout << "message: " << ex.what() << std::endl; + continue; + } + std::cout << "FAILURE - query should throw an exception" << std::endl; + } +} + diff --git a/parser/test.sh b/parser/test.sh new file mode 100755 index 00000000..aad1d031 --- /dev/null +++ b/parser/test.sh @@ -0,0 +1,3 @@ +# /bin/sh +llvm-g++ -std=c++11 -I ../../../vendor/PEGTL/ -o test test.cpp parser.cpp +./test From 113510991a702cf5a17d0be59049aaac75493e75 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Thu, 12 Nov 2015 14:24:37 -0800 Subject: [PATCH 21/66] more grammer tests --- parser/parser.cpp | 9 ++++----- parser/test.cpp | 37 ++++++++++++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/parser/parser.cpp b/parser/parser.cpp index ef15fd48..9443a37c 100644 --- a/parser/parser.cpp +++ b/parser/parser.cpp @@ -31,15 +31,14 @@ namespace parser { // strings struct unicode : list< seq< one< 'u' >, rep< 4, must< xdigit > > >, one< '\\' > > {}; -struct escaped_char : one< '"', '\'', '\\', '/', 'b', 'f', 'n', 'r', 't' > {}; +struct escaped_char : one< '"', '\'', '\\', '/', 'b', 'f', 'n', 'r', 't', '0' > {}; struct escaped : sor< escaped_char, unicode > {}; struct unescaped : utf8::range< 0x20, 0x10FFFF > {}; -struct char_ : if_then_else< one< '\\' >, must< escaped >, unescaped > {}; - -struct dq_string_content : until< at< one< '"' > >, must< char_ > > {}; +struct chars : if_then_else< one< '\\' >, must< escaped >, unescaped > {}; +struct dq_string_content : until< at< one< '"' > >, must< chars > > {}; struct dq_string : seq< one< '"' >, must< dq_string_content >, any > {}; -struct sq_string_content : until< at< one< '\'' > >, must< char_ > > {}; +struct sq_string_content : until< at< one< '\'' > >, must< chars > > {}; struct sq_string : seq< one< '\'' >, must< sq_string_content >, any > {}; // numbers diff --git a/parser/test.cpp b/parser/test.cpp index 23494306..9655ee05 100644 --- a/parser/test.cpp +++ b/parser/test.cpp @@ -7,15 +7,50 @@ #include static std::vector valid_queries = { + // true/false predicates "truepredicate", "falsepredicate", "TRUEPREDICATE", "FALSEPREDICATE", - "truepredicate && truepredicate" + + // characters/strings + "\"\" = ''", + "'azAZ09/ :()[]{}<>,.^@-+=*&~`' = '\\\" \\' \\\\ \\/ \\b \\f \\n \\r \\t \\0'", + "\"azAZ09/\" = \"\\\" \\' \\\\ \\/ \\b \\f \\n \\r \\t \\0\"", + "'\\uffFf' = '\\u0020'", + "'\\u01111' = 'asdf\\u0111asdf'", + + // numbers, bools, keypaths + "-1 = 12", + "0 = 001", + "0x0 = -0X398235fcAb", + "10. = -.034", + "10.0 = 5.034", + "true = false", + "_ = a", + "_a = _.aZ", + "a09._br.z = __-__.Z-9", }; static std::vector invalid_queries = { "predicate", + "'\\a' = ''", // invalid escape + + // invalid unicode + "'\\u0' = ''", + + // invalid strings + "\"' = ''", + "\" = ''", + "' = ''", + + // invalid numbers, bools, keypaths + "03a = 1", + "1..0 = 1", + "1.0. = 1", + "0x = 1", + "truey = false", + "truepredicate &&", "truepredicate & truepredicate", }; From c6899d25d7ed36056115af2fe83ecd0588c6eb47 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Thu, 12 Nov 2015 14:34:47 -0800 Subject: [PATCH 22/66] fix and tests for arguments --- parser/parser.cpp | 4 ++-- parser/test.cpp | 17 +++++++++++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/parser/parser.cpp b/parser/parser.cpp index 9443a37c..5bc6e7d8 100644 --- a/parser/parser.cpp +++ b/parser/parser.cpp @@ -61,8 +61,8 @@ struct false_value : pegtl_istring_t("false") {}; struct key_path : list< seq< sor< alpha, one< '_' > >, star< sor< alnum, one< '_', '-' > > > >, one< '.' > > {}; // argument -struct argument_index : until< at< one< '}' > >, must< digit > > {}; -struct argument : seq< one< '{' >, must< argument_index >, any > {}; +struct argument_index : plus< digit > {}; +struct argument : seq< one< '{' >, must< argument_index, one< '}' > > > {}; // expressions and operators struct expr : sor< dq_string, sq_string, number, argument, true_value, false_value, key_path > {}; diff --git a/parser/test.cpp b/parser/test.cpp index 9655ee05..3870569f 100644 --- a/parser/test.cpp +++ b/parser/test.cpp @@ -20,7 +20,7 @@ static std::vector valid_queries = { "'\\uffFf' = '\\u0020'", "'\\u01111' = 'asdf\\u0111asdf'", - // numbers, bools, keypaths + // expressions (numbers, bools, keypaths, arguments) "-1 = 12", "0 = 001", "0x0 = -0X398235fcAb", @@ -30,6 +30,10 @@ static std::vector valid_queries = { "_ = a", "_a = _.aZ", "a09._br.z = __-__.Z-9", + + // arguments + "{0} = {19}", + "{0} = {0}", }; static std::vector invalid_queries = { @@ -44,12 +48,21 @@ static std::vector invalid_queries = { "\" = ''", "' = ''", - // invalid numbers, bools, keypaths + // expressions "03a = 1", "1..0 = 1", "1.0. = 1", + "1-0 = 1", "0x = 1", "truey = false", + "- = a", + "a..b = a", + "a$a = a", + "{} = {0}", + "{-1} = {0}", + "{a} = {0}", + "{ = }", + "truepredicate &&", "truepredicate & truepredicate", From 293552b37b9e2d782a60fae3f4052c36bc7a2793 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Thu, 12 Nov 2015 14:51:31 -0800 Subject: [PATCH 23/66] tests for all expressions/operators --- parser/test.cpp | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/parser/test.cpp b/parser/test.cpp index 3870569f..830ad13a 100644 --- a/parser/test.cpp +++ b/parser/test.cpp @@ -10,8 +10,8 @@ static std::vector valid_queries = { // true/false predicates "truepredicate", "falsepredicate", - "TRUEPREDICATE", - "FALSEPREDICATE", + " TRUEPREDICATE ", + " FALSEPREDICATE ", // characters/strings "\"\" = ''", @@ -30,10 +30,27 @@ static std::vector valid_queries = { "_ = a", "_a = _.aZ", "a09._br.z = __-__.Z-9", - - // arguments "{0} = {19}", "{0} = {0}", + + // operators + "0=0", + "0 = 0", + "0!=0", + "0 != 0", + "0==0", + "0 == 0", + "0>0", + "0 > 0", + "0>=0", + "0 >= 0", + "0<0", + "0 < 0", + "0<=0", + "0 <= 0", + "0 contains 0", + "0 BeGiNsWiTh 0", + "0 ENDSWITH 0", }; static std::vector invalid_queries = { @@ -63,6 +80,11 @@ static std::vector invalid_queries = { "{a} = {0}", "{ = }", + // operators + "0===>0", + "0 <> 0", + "0 contains1", + "endswith 0", "truepredicate &&", "truepredicate & truepredicate", From fcf77f01c70e4630921dacdefed268f635da6cb6 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Thu, 12 Nov 2015 15:04:15 -0800 Subject: [PATCH 24/66] change argument syntax to use $ instead of {} --- parser/parser.cpp | 2 +- parser/test.cpp | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/parser/parser.cpp b/parser/parser.cpp index 5bc6e7d8..94e3f4b4 100644 --- a/parser/parser.cpp +++ b/parser/parser.cpp @@ -62,7 +62,7 @@ struct key_path : list< seq< sor< alpha, one< '_' > >, star< sor< alnum, one< '_ // argument struct argument_index : plus< digit > {}; -struct argument : seq< one< '{' >, must< argument_index, one< '}' > > > {}; +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 > {}; diff --git a/parser/test.cpp b/parser/test.cpp index 830ad13a..b5fb6458 100644 --- a/parser/test.cpp +++ b/parser/test.cpp @@ -30,8 +30,8 @@ static std::vector valid_queries = { "_ = a", "_a = _.aZ", "a09._br.z = __-__.Z-9", - "{0} = {19}", - "{0} = {0}", + "$0 = $19", + "$0=$0", // operators "0=0", @@ -75,10 +75,10 @@ static std::vector invalid_queries = { "- = a", "a..b = a", "a$a = a", - "{} = {0}", - "{-1} = {0}", - "{a} = {0}", - "{ = }", + "{} = $0", + "$-1 = $0", + "$a = $0", + "$ = $", // operators "0===>0", From b926b602d959706764162ccbfe5378fdac19f5d8 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Thu, 12 Nov 2015 15:19:32 -0800 Subject: [PATCH 25/66] test not, remove requirement of padding --- parser/parser.cpp | 2 +- parser/test.cpp | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/parser/parser.cpp b/parser/parser.cpp index 94e3f4b4..bc4d6520 100644 --- a/parser/parser.cpp +++ b/parser/parser.cpp @@ -91,7 +91,7 @@ struct group_pred : if_must< one< '(' >, pad< pred, blank >, one< ')' > > {}; struct true_pred : pegtl_istring_t("truepredicate") {}; struct false_pred : pegtl_istring_t("falsepredicate") {}; -struct not_pre : sor< seq< one< '!' >, star< blank > >, seq< pegtl_istring_t("not"), plus< blank > > > {}; +struct not_pre : seq< sor< one< '!' >, seq< pegtl_istring_t("not") >, star< blank > > > {}; struct atom_pred : seq< opt< not_pre >, pad< sor< group_pred, true_pred, false_pred, comparison_pred >, blank > > {}; struct and_op : sor< pad< two< '&' >, blank >, pad_plus< pegtl_istring_t("and"), blank > > {}; diff --git a/parser/test.cpp b/parser/test.cpp index b5fb6458..636ea316 100644 --- a/parser/test.cpp +++ b/parser/test.cpp @@ -51,6 +51,20 @@ static std::vector valid_queries = { "0 contains 0", "0 BeGiNsWiTh 0", "0 ENDSWITH 0", + + // atoms/groups + "(0=0)", + "( 0=0 )", + "((0=0))", + "!0=0", + "! 0=0", + "!(0=0)", + "! (0=0)", + "NOT0=0", // keypath NOT0 + "not 0=0", + "NOT(0=0)", + "not (0=0)", + "NOT (!0=0)", }; static std::vector invalid_queries = { @@ -86,6 +100,15 @@ static std::vector invalid_queries = { "0 contains1", "endswith 0", + // atoms/groups + "0=0)", + "(0=0", + "(0=0))", + "! =0", + "NOTNOT(0=0)", + "(!!0=0)", + "0=0 !", + "truepredicate &&", "truepredicate & truepredicate", }; From 6a97f91ef978554181d29d33f03b469059bd61bf Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Thu, 12 Nov 2015 15:40:54 -0800 Subject: [PATCH 26/66] compound tests --- parser/parser.cpp | 4 ++-- parser/test.cpp | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/parser/parser.cpp b/parser/parser.cpp index bc4d6520..19b6b226 100644 --- a/parser/parser.cpp +++ b/parser/parser.cpp @@ -94,8 +94,8 @@ struct false_pred : pegtl_istring_t("falsepredicate") {}; struct not_pre : seq< sor< one< '!' >, seq< pegtl_istring_t("not") >, star< blank > > > {}; struct atom_pred : seq< opt< not_pre >, pad< sor< group_pred, true_pred, false_pred, comparison_pred >, blank > > {}; -struct and_op : sor< pad< two< '&' >, blank >, pad_plus< pegtl_istring_t("and"), blank > > {}; -struct or_op : sor< pad< two< '|' >, blank> , pad_plus< pegtl_istring_t("or"), blank > > {}; +struct and_op : pad< sor< two< '&' >, pegtl_istring_t("and") >, blank > {}; +struct or_op : pad< sor< two< '|' >, pegtl_istring_t("or") >, blank > {}; struct or_ext : if_must< or_op, pred > {}; struct and_ext : if_must< and_op, pred > {}; diff --git a/parser/test.cpp b/parser/test.cpp index 636ea316..e47dcee7 100644 --- a/parser/test.cpp +++ b/parser/test.cpp @@ -65,6 +65,15 @@ static std::vector valid_queries = { "NOT(0=0)", "not (0=0)", "NOT (!0=0)", + + // compound + "a==a && a==a", + "a==a || a==a", + "a==a&&a==a||a=a", + "a==a and a==a", + "a==a OR a==a", + "and=='AND'&&'or'=='||'", + "and == or && ORE > GRAND", }; static std::vector invalid_queries = { @@ -109,6 +118,13 @@ static std::vector invalid_queries = { "(!!0=0)", "0=0 !", + // compound + "a==a & a==a", + "a==a | a==a", + "a==a &| a==a", + "a==a && OR a==a", + "a==aORa==a", + "truepredicate &&", "truepredicate & truepredicate", }; From fdf1fbd12b694a3f233a455ac7341dc1beff32e3 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Mon, 16 Nov 2015 14:48:12 -0800 Subject: [PATCH 27/66] fix for not predicate --- parser/parser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parser/parser.cpp b/parser/parser.cpp index 19b6b226..b391c9b4 100644 --- a/parser/parser.cpp +++ b/parser/parser.cpp @@ -91,7 +91,7 @@ struct group_pred : if_must< one< '(' >, pad< pred, blank >, one< ')' > > {}; struct true_pred : pegtl_istring_t("truepredicate") {}; struct false_pred : pegtl_istring_t("falsepredicate") {}; -struct not_pre : seq< sor< one< '!' >, seq< pegtl_istring_t("not") >, star< blank > > > {}; +struct not_pre : seq< sor< one< '!' >, pegtl_istring_t("not") > > {}; struct atom_pred : seq< opt< not_pre >, pad< sor< group_pred, true_pred, false_pred, comparison_pred >, blank > > {}; struct and_op : pad< sor< two< '&' >, pegtl_istring_t("and") >, blank > {}; From aeb1e7ecb35d89eece5e799ec41b2016a419db03 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Tue, 17 Nov 2015 17:17:54 -0800 Subject: [PATCH 28/66] bool tests --- parser/query_builder.cpp | 3 +++ parser/test.cpp | 2 ++ 2 files changed, 5 insertions(+) diff --git a/parser/query_builder.cpp b/parser/query_builder.cpp index 5afdfcb9..a836f777 100644 --- a/parser/query_builder.cpp +++ b/parser/query_builder.cpp @@ -220,6 +220,9 @@ struct ValueGetter { if (value.type == parser::Expression::Type::Argument) { return args.bool_for_argument(std::stoi(value.s)); } + if (value.type != parser::Expression::Type::True && value.type != parser::Expression::Type::False) { + throw std::runtime_error("Attempting to compare bool property to a non-bool value"); + } return value.type == parser::Expression::Type::True; } }; diff --git a/parser/test.cpp b/parser/test.cpp index e47dcee7..f18a36ff 100644 --- a/parser/test.cpp +++ b/parser/test.cpp @@ -74,6 +74,7 @@ static std::vector valid_queries = { "a==a OR a==a", "and=='AND'&&'or'=='||'", "and == or && ORE > GRAND", + "a=1AND NOTb=2", }; static std::vector invalid_queries = { @@ -124,6 +125,7 @@ static std::vector invalid_queries = { "a==a &| a==a", "a==a && OR a==a", "a==aORa==a", + "a=1ANDNOT b=2", "truepredicate &&", "truepredicate & truepredicate", From c2e5a268a640bd14f22eafe1defb6f845bf7a995 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Wed, 18 Nov 2015 12:17:39 -0800 Subject: [PATCH 29/66] first string tests and custom error messages --- parser/parser.cpp | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/parser/parser.cpp b/parser/parser.cpp index b391c9b4..8d1c3be6 100644 --- a/parser/parser.cpp +++ b/parser/parser.cpp @@ -288,17 +288,34 @@ template<> struct action< not_pre > } }; +template< typename Rule > +struct error_message_control : pegtl::normal< Rule > +{ + static const std::string error_message; + + template< typename Input, typename ... States > + static void raise( const Input & in, States && ... ) + { + throw pegtl::parse_error( error_message, in ); + } +}; + +template<> +const std::string error_message_control< chars >::error_message = "Invalid characters in string constant."; + +template< typename Rule> +const std::string error_message_control< Rule >::error_message = "Invalid predicate."; + Predicate parse(const std::string &query) { analyze< pred >(); - const std::string source = "user query"; Predicate out_predicate(Predicate::Type::And); ParserState state; state.predicate_stack.push_back(&out_predicate); - pegtl::parse< must< pred, eof >, action >(query, source, state); + 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) { return std::move(out_predicate.cpnd.sub_predicates.back()); } From f69dc9c081976f2fa2d1dae4848cc010fc426ed6 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Wed, 18 Nov 2015 12:40:25 -0800 Subject: [PATCH 30/66] more string tests and bugfix --- parser/query_builder.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/parser/query_builder.cpp b/parser/query_builder.cpp index a836f777..9039ef6b 100644 --- a/parser/query_builder.cpp +++ b/parser/query_builder.cpp @@ -267,6 +267,9 @@ struct ValueGetter { if (value.type == parser::Expression::Type::Argument) { return args.string_for_argument(std::stoi(value.s)); } + if (value.type != parser::Expression::Type::String) { + throw std::runtime_error("Attempting to compare String property to a non-String value"); + } return value.s; } }; From 3ac196166b05ac17c81620da05e21fee117b98b6 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Wed, 18 Nov 2015 17:49:05 -0800 Subject: [PATCH 31/66] add binary query support --- parser/query_builder.cpp | 79 +++++++++++++++++++++++++++++++++------- parser/query_builder.hpp | 2 + 2 files changed, 68 insertions(+), 13 deletions(-) diff --git a/parser/query_builder.cpp b/parser/query_builder.cpp index 9039ef6b..353588e8 100644 --- a/parser/query_builder.cpp +++ b/parser/query_builder.cpp @@ -102,42 +102,81 @@ void add_bool_constraint_to_query(Query &query, Predicate::Operator operatorType void add_string_constraint_to_query(Query &query, Predicate::Operator op, Columns &&column, - StringData value) { + std::string value) { bool case_sensitive = true; - StringData sd = value; switch (op) { case Predicate::Operator::BeginsWith: - query.and_query(column.begins_with(sd, case_sensitive)); + query.and_query(column.begins_with(value, case_sensitive)); break; case Predicate::Operator::EndsWith: - query.and_query(column.ends_with(sd, case_sensitive)); + query.and_query(column.ends_with(value, case_sensitive)); break; case Predicate::Operator::Contains: - query.and_query(column.contains(sd, case_sensitive)); + query.and_query(column.contains(value, case_sensitive)); break; case Predicate::Operator::Equal: - query.and_query(column.equal(sd, case_sensitive)); + query.and_query(column.equal(value, case_sensitive)); break; case Predicate::Operator::NotEqual: - query.and_query(column.not_equal(sd, case_sensitive)); + query.and_query(column.not_equal(value, case_sensitive)); break; default: throw std::runtime_error("Unsupported operator for string queries."); } } -void add_string_constraint_to_query(realm::Query& query, +void add_string_constraint_to_query(realm::Query &query, Predicate::Operator op, - StringData value, + std::string value, Columns &&column) { bool case_sensitive = true; - StringData sd = value; switch (op) { case Predicate::Operator::Equal: - query.and_query(column.equal(sd, case_sensitive)); + query.and_query(column.equal(value, case_sensitive)); break; case Predicate::Operator::NotEqual: - query.and_query(column.not_equal(sd, case_sensitive)); + query.and_query(column.not_equal(value, case_sensitive)); + break; + default: + throw std::runtime_error("Substring comparison not supported for keypath substrings."); + } +} + +void add_binary_constraint_to_query(Query &query, + Predicate::Operator op, + Columns &&column, + std::string value) { + switch (op) { + case Predicate::Operator::BeginsWith: + query.begins_with(column.m_column, value); + break; + case Predicate::Operator::EndsWith: + query.ends_with(column.m_column, value); + break; + case Predicate::Operator::Contains: + query.contains(column.m_column, value); + break; + case Predicate::Operator::Equal: + query.equal(column.m_column, value); + break; + case Predicate::Operator::NotEqual: + query.not_equal(column.m_column, value); + break; + default: + throw std::runtime_error("Unsupported operator for binary queries."); + } +} + +void add_binary_constraint_to_query(realm::Query &query, + Predicate::Operator op, + std::string value, + Columns &&column) { + switch (op) { + case Predicate::Operator::Equal: + query.equal(column.m_column, value); + break; + case Predicate::Operator::NotEqual: + query.not_equal(column.m_column, value); break; default: throw std::runtime_error("Substring comparison not supported for keypath substrings."); @@ -274,6 +313,17 @@ struct ValueGetter { } }; +template +struct ValueGetter { + static std::string convert(TableGetter&&, const parser::Expression & value, Arguments &args) + { + if (value.type == parser::Expression::Type::Argument) { + return args.binary_for_argument(std::stoi(value.s)); + } + throw std::runtime_error("Binary properties must be compared against a binary argument."); + } +}; + template auto value_of_type_for_query(TableGetter&& tables, Value&& value, Arguments &args) { @@ -309,10 +359,13 @@ void do_add_comparison_to_query(Query &query, Schema &schema, ObjectSchema &obje value_of_type_for_query(expr.table_getter, rhs, args)); break; case PropertyTypeString: - case PropertyTypeData: 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)); 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)); + break; default: { throw std::runtime_error((std::string)"Object type " + string_for_property_type(type) + " not supported"); } diff --git a/parser/query_builder.hpp b/parser/query_builder.hpp index 1de648b3..2d7f9d7c 100644 --- a/parser/query_builder.hpp +++ b/parser/query_builder.hpp @@ -40,6 +40,7 @@ namespace realm { virtual float float_for_argument(size_t argument_index) = 0; virtual double double_for_argument(size_t argument_index) = 0; virtual std::string string_for_argument(size_t argument_index) = 0; + virtual std::string binary_for_argument(size_t argument_index) = 0; virtual DateTime datetime_for_argument(size_t argument_index) = 0; virtual size_t object_index_for_argument(size_t argument_index) = 0; virtual bool is_argument_null(size_t argument_index) = 0; @@ -57,6 +58,7 @@ namespace realm { virtual float float_for_argument(size_t argument_index) { return Accessor::to_float(m_ctx, argument_at(argument_index)); } virtual double double_for_argument(size_t argument_index) { return Accessor::to_double(m_ctx, argument_at(argument_index)); } virtual std::string string_for_argument(size_t argument_index) { return Accessor::to_string(m_ctx, argument_at(argument_index)); } + virtual std::string binary_for_argument(size_t argument_index) { return Accessor::to_binary(m_ctx, argument_at(argument_index)); } virtual DateTime datetime_for_argument(size_t argument_index) { return Accessor::to_datetime(m_ctx, argument_at(argument_index)); } virtual size_t object_index_for_argument(size_t argument_index) { return Accessor::to_existing_object_index(m_ctx, argument_at(argument_index)); } virtual bool is_argument_null(size_t argument_index) { return Accessor::is_null(m_ctx, argument_at(argument_index)); } From 88730cf0a5dde7d18001a3c0598ff65c43f97ba7 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Thu, 19 Nov 2015 15:17:57 -0800 Subject: [PATCH 32/66] test and bug fixes for data queries --- parser/query_builder.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/parser/query_builder.cpp b/parser/query_builder.cpp index 353588e8..59425aaa 100644 --- a/parser/query_builder.cpp +++ b/parser/query_builder.cpp @@ -102,7 +102,7 @@ void add_bool_constraint_to_query(Query &query, Predicate::Operator operatorType void add_string_constraint_to_query(Query &query, Predicate::Operator op, Columns &&column, - std::string value) { + std::string &&value) { bool case_sensitive = true; switch (op) { case Predicate::Operator::BeginsWith: @@ -127,7 +127,7 @@ void add_string_constraint_to_query(Query &query, void add_string_constraint_to_query(realm::Query &query, Predicate::Operator op, - std::string value, + std::string &&value, Columns &&column) { bool case_sensitive = true; switch (op) { @@ -145,22 +145,22 @@ void add_string_constraint_to_query(realm::Query &query, void add_binary_constraint_to_query(Query &query, Predicate::Operator op, Columns &&column, - std::string value) { + std::string &&value) { switch (op) { case Predicate::Operator::BeginsWith: - query.begins_with(column.m_column, value); + query.begins_with(column.m_column, BinaryData(value)); break; case Predicate::Operator::EndsWith: - query.ends_with(column.m_column, value); + query.ends_with(column.m_column, BinaryData(value)); break; case Predicate::Operator::Contains: - query.contains(column.m_column, value); + query.contains(column.m_column, BinaryData(value)); break; case Predicate::Operator::Equal: - query.equal(column.m_column, value); + query.equal(column.m_column, BinaryData(value)); break; case Predicate::Operator::NotEqual: - query.not_equal(column.m_column, value); + query.not_equal(column.m_column, BinaryData(value)); break; default: throw std::runtime_error("Unsupported operator for binary queries."); @@ -173,10 +173,10 @@ void add_binary_constraint_to_query(realm::Query &query, Columns &&column) { switch (op) { case Predicate::Operator::Equal: - query.equal(column.m_column, value); + query.equal(column.m_column, BinaryData(value)); break; case Predicate::Operator::NotEqual: - query.not_equal(column.m_column, value); + query.not_equal(column.m_column, BinaryData(value)); break; default: throw std::runtime_error("Substring comparison not supported for keypath substrings."); From 6715a9b786fb36cfbdbc9c5ddbe988855f5388dc Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Thu, 19 Nov 2015 15:59:16 -0800 Subject: [PATCH 33/66] turn off parser analyze --- parser/parser.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/parser/parser.cpp b/parser/parser.cpp index 8d1c3be6..7d771285 100644 --- a/parser/parser.cpp +++ b/parser/parser.cpp @@ -308,8 +308,6 @@ const std::string error_message_control< Rule >::error_message = "Invalid predic Predicate parse(const std::string &query) { - analyze< pred >(); - Predicate out_predicate(Predicate::Type::And); ParserState state; From 6d63042d7e5b7714d34632b42dcc6200e4af5dc2 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Fri, 20 Nov 2015 14:14:35 -0800 Subject: [PATCH 34/66] object tests --- parser/query_builder.cpp | 145 ++++++++++++++++++++++++++------------- 1 file changed, 96 insertions(+), 49 deletions(-) diff --git a/parser/query_builder.cpp b/parser/query_builder.cpp index 59425aaa..d7f26e07 100644 --- a/parser/query_builder.cpp +++ b/parser/query_builder.cpp @@ -53,6 +53,51 @@ struct FalseExpression : realm::Expression { const Table* get_table() const override { return nullptr; } }; +using KeyPath = std::vector; +KeyPath key_path_from_string(const std::string &s) { + std::stringstream ss(s); + std::string item; + KeyPath key_path; + while (std::getline(ss, item, '.')) { + key_path.push_back(item); + } + return key_path; +} + +struct PropertyExpression +{ + Property *prop = nullptr; + std::vector indexes; + std::function
table_getter; + + PropertyExpression(Query &query, Schema &schema, Schema::iterator desc, const std::string &key_path_string) + { + KeyPath key_path = key_path_from_string(key_path_string); + for (size_t index = 0; index < key_path.size(); index++) { + if (prop) { + precondition(prop->type == PropertyTypeObject || prop->type == PropertyTypeArray, + (std::string)"Property '" + key_path[index] + "' is not a link in object of type '" + desc->name + "'"); + indexes.push_back(prop->table_column); + + } + prop = desc->property_for_name(key_path[index]); + precondition(prop != nullptr, "No property '" + key_path[index] + "' on object of type '" + desc->name + "'"); + + if (prop->object_type.size()) { + desc = schema.find(prop->object_type); + } + } + + table_getter = [&] { + TableRef& tbl = query.get_table(); + for (size_t col : indexes) { + tbl->link(col); // mutates m_link_chain on table + } + return tbl.get(); + }; + } +}; + // add a clause for numeric constraints based on operator type template @@ -183,51 +228,48 @@ void add_binary_constraint_to_query(realm::Query &query, } } - -using KeyPath = std::vector; -KeyPath key_path_from_string(const std::string &s) { - std::stringstream ss(s); - std::string item; - KeyPath key_path; - while (std::getline(ss, item, '.')) { - key_path.push_back(item); +void add_link_constraint_to_query(realm::Query &query, + Predicate::Operator op, + PropertyExpression &prop_expr, + size_t row_index) { + 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.links_to(prop_expr.prop->table_column, row_index); + break; + default: + throw std::runtime_error("Only 'equal' and 'not equal' operators supported for object comparison."); } - return key_path; } -struct PropertyExpression -{ - Property *prop = nullptr; - std::vector indexes; - std::function
table_getter; - - PropertyExpression(Query &query, Schema &schema, ObjectSchema &desc, const std::string &key_path_string) - { - KeyPath key_path = key_path_from_string(key_path_string); - for (size_t index = 0; index < key_path.size(); index++) { - if (prop) { - precondition(prop->type == PropertyTypeObject || prop->type == PropertyTypeArray, - (std::string)"Property '" + key_path[index] + "' is not a link in object of type '" + desc.name + "'"); - indexes.push_back(prop->table_column); - - } - prop = desc.property_for_name(key_path[index]); - precondition(prop != nullptr, "No property '" + key_path[index] + "' on object of type '" + desc.name + "'"); - - if (prop->object_type.size()) { - desc = *schema.find(prop->object_type); - } - } - - table_getter = [&] { - TableRef& tbl = query.get_table(); - for (size_t col : indexes) { - tbl->link(col); // mutates m_link_chain on table - } - return tbl.get(); - }; +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(PropertyExpression &propExpr, parser::Expression &argExpr, Arguments &args) +{ + return args.object_index_for_argument(std::stoi(argExpr.s)); +} + +auto link_argument(parser::Expression &argExpr, PropertyExpression &propExpr, Arguments &args) +{ + return args.object_index_for_argument(std::stoi(argExpr.s)); +} + template struct ColumnGetter { @@ -366,30 +408,35 @@ void do_add_comparison_to_query(Query &query, Schema &schema, ObjectSchema &obje 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)); break; + case PropertyTypeObject: + case PropertyTypeArray: + add_link_constraint_to_query(query, op, expr, link_argument(lhs, rhs, args)); + break; default: { throw std::runtime_error((std::string)"Object type " + string_for_property_type(type) + " not supported"); } } } -void add_comparison_to_query(Query &query, Predicate &pred, Arguments &args, Schema &schema, ObjectSchema &object_schema) +void add_comparison_to_query(Query &query, Predicate &pred, Arguments &args, Schema &schema, const std::string &type) { Predicate::Comparison &cmpr = pred.cmpr; auto t0 = cmpr.expr[0].type, t1 = cmpr.expr[1].type; + 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.op, 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.op, expr, cmpr.expr[0], expr, args); } else { throw std::runtime_error("Predicate expressions must compare a keypath and another keypath or a constant value"); } } -void update_query_with_predicate(Query &query, Predicate &pred, Arguments &arguments, Schema &schema, ObjectSchema &object_schema) +void update_query_with_predicate(Query &query, Predicate &pred, Arguments &arguments, Schema &schema, const std::string &type) { if (pred.negate) { query.Not(); @@ -399,7 +446,7 @@ void update_query_with_predicate(Query &query, Predicate &pred, Arguments &argum case Predicate::Type::And: query.group(); for (auto &sub : pred.cpnd.sub_predicates) { - update_query_with_predicate(query, sub, arguments, schema, object_schema); + update_query_with_predicate(query, sub, arguments, schema, type); } if (!pred.cpnd.sub_predicates.size()) { query.and_query(new TrueExpression); @@ -411,7 +458,7 @@ void update_query_with_predicate(Query &query, Predicate &pred, Arguments &argum query.group(); for (auto &sub : pred.cpnd.sub_predicates) { query.Or(); - update_query_with_predicate(query, sub, arguments, schema, object_schema); + update_query_with_predicate(query, sub, arguments, schema, type); } if (!pred.cpnd.sub_predicates.size()) { query.and_query(new FalseExpression); @@ -420,7 +467,7 @@ void update_query_with_predicate(Query &query, Predicate &pred, Arguments &argum break; case Predicate::Type::Comparison: { - add_comparison_to_query(query, pred, arguments, schema, object_schema); + add_comparison_to_query(query, pred, arguments, schema, type); break; } case Predicate::Type::True: @@ -439,7 +486,7 @@ void update_query_with_predicate(Query &query, Predicate &pred, Arguments &argum void apply_predicate(Query &query, Predicate &predicate, Arguments &arguments, Schema &schema, std::string objectType) { - update_query_with_predicate(query, predicate, arguments, schema, *schema.find(objectType)); + update_query_with_predicate(query, predicate, arguments, schema, objectType); // Test the constructed query in core std::string validateMessage = query.validate(); From 12176e96e976947a0b4840cf1a15928b6a74dae5 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Fri, 20 Nov 2015 14:51:04 -0800 Subject: [PATCH 35/66] move queryTests to parser dir --- parser/queryTests.json | 250 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 250 insertions(+) create mode 100644 parser/queryTests.json diff --git a/parser/queryTests.json b/parser/queryTests.json new file mode 100644 index 00000000..f9a5f56f --- /dev/null +++ b/parser/queryTests.json @@ -0,0 +1,250 @@ +{ + +"dateTests" : { + "schema" : [{ + "name": "DateObject", + "properties": [{ "name": "date", "type": "date" }] + }], + "objects": [ + { "type": "DateObject", "value": [10000] }, + { "type": "DateObject", "value": [10001] }, + { "type": "DateObject", "value": [10002] } + ], + "tests": [ + ["QueryCount", 2, "DateObject", "date < $0", [2, "date"]], + ["QueryCount", 3, "DateObject", "date <= $0", [2, "date"]], + ["QueryCount", 2, "DateObject", "date > $0", [0, "date"]], + ["QueryCount", 3, "DateObject", "date >= $0", [0, "date"]], + ["QueryCount", 1, "DateObject", "date == $0", [0, "date"]], + ["QueryCount", 2, "DateObject", "date != $0", [0, "date"]], + + ["QueryThrows", "DateObject", "date == 'not a date'"], + ["QueryThrows", "DateObject", "date == 1"], + ["QueryThrows", "DateObject", "date == $0", 1] + ] +}, + +"boolTests" : { + "schema" : [{ + "name": "BoolObject", + "properties": [{ "name": "boolCol", "type": "bool" }] + }], + "objects": [ + { "type": "BoolObject", "value": [false] }, + { "type": "BoolObject", "value": [true] }, + { "type": "BoolObject", "value": [true] } + ], + "tests": [ + ["QueryCount", 2, "BoolObject", "boolCol == true"], + ["QueryCount", 1, "BoolObject", "boolCol==false"], + ["QueryCount", 1, "BoolObject", "boolCol != true"], + ["QueryCount", 2, "BoolObject", "true == boolCol"], + ["QueryCount", 2, "BoolObject", "boolCol == TRUE"], + ["QueryCount", 1, "BoolObject", "boolCol == FALSE"], + ["QueryCount", 2, "BoolObject", "boolCol == $0", true], + ["QueryCount", 1, "BoolObject", "boolCol == $0", false], + ["QueryCount", 0, "BoolObject", "boolCol == true && boolCol == false"], + ["QueryCount", 3, "BoolObject", "boolCol == true || boolCol == false"], + + ["QueryThrows", "BoolObject", "boolCol == 0"], + ["QueryThrows", "BoolObject", "boolCol == 1"], + ["QueryThrows", "BoolObject", "boolCol == 'not a bool'"], + ["QueryThrows", "BoolObject", "boolCol == $0", "not a bool"], + ["QueryThrows", "BoolObject", "boolCol > true"], + ["QueryThrows", "BoolObject", "boolCol >= true"], + ["QueryThrows", "BoolObject", "boolCol < true"], + ["QueryThrows", "BoolObject", "boolCol <= true"], + ["QueryThrows", "BoolObject", "boolCol BEGINSWITH true"], + ["QueryThrows", "BoolObject", "boolCol CONTAINS true"], + ["QueryThrows", "BoolObject", "boolCol ENDSWITH true"] + ] +}, + +"intTests" : { + "schema" : [{ + "name": "IntObject", + "properties": [{ "name": "intCol", "type": "int" }] + }], + "objects": [ + { "type": "IntObject", "value": [-1] }, + { "type": "IntObject", "value": [0] }, + { "type": "IntObject", "value": [100] } + ], + "tests": [ + ["QueryCount", 1, "IntObject", "intCol == -1"], + ["QueryCount", 1, "IntObject", "intCol==0"], + ["QueryCount", 0, "IntObject", "1 == intCol"], + ["QueryCount", 2, "IntObject", "intCol != 0"], + ["QueryCount", 2, "IntObject", "intCol > -1"], + ["QueryCount", 3, "IntObject", "intCol >= -1"], + ["QueryCount", 2, "IntObject", "intCol < 100"], + ["QueryCount", 3, "IntObject", "intCol <= 100"], + ["QueryCount", 1, "IntObject", "intCol > 0x1F"], + ["QueryCount", 1, "IntObject", "intCol == $0", 100], + + ["QueryThrows", "IntObject", "intCol == 'not an int'"], + ["QueryThrows", "IntObject", "intCol == true"], + ["QueryThrows", "IntObject", "intCol == $0", "not an int"], + ["QueryThrows", "IntObject", "intCol BEGINSWITH 1"], + ["QueryThrows", "IntObject", "intCol CONTAINS 1"], + ["QueryThrows", "IntObject", "intCol ENDSWITH 1"] + ] +}, + +"floatTests" : { + "schema" : [{ + "name": "FloatObject", + "properties": [{ "name": "floatCol", "type": "float" }] + }], + "objects": [ + { "type": "FloatObject", "value": [-1.001] }, + { "type": "FloatObject", "value": [0.0] }, + { "type": "FloatObject", "value": [100.2] } + ], + "tests": [ + ["QueryCount", 1, "FloatObject", "floatCol == -1.001"], + ["QueryCount", 1, "FloatObject", "floatCol = 0"], + ["QueryCount", 0, "FloatObject", "1 == floatCol"], + ["QueryCount", 2, "FloatObject", "floatCol != 0"], + ["QueryCount", 2, "FloatObject", "floatCol > -1.001"], + ["QueryCount", 3, "FloatObject", "floatCol >= -1.001"], + ["QueryCount", 2, "FloatObject", "floatCol < 100.2"], + ["QueryCount", 3, "FloatObject", "floatCol <= 100.2"], + ["QueryCount", 1, "FloatObject", "floatCol > 0x1F"], + ["QueryCount", 1, "FloatObject", "floatCol == $0", 100.2], + + ["QueryThrows", "FloatObject", "floatCol == 'not a float'"], + ["QueryThrows", "FloatObject", "floatCol == true"], + ["QueryThrows", "FloatObject", "floatCol == $0", "not a float"], + ["QueryThrows", "FloatObject", "floatCol BEGINSWITH 1"], + ["QueryThrows", "FloatObject", "floatCol CONTAINS 1"], + ["QueryThrows", "FloatObject", "floatCol ENDSWITH 1"], + + ["Disabled", "QueryThrows", "FloatObject", "floatCol = 3.5e+38"], + ["Disabled", "QueryThrows", "FloatObject", "floatCol = -3.5e+38"] + ] +}, + +"doubleTests" : { + "schema" : [{ + "name": "DoubleObject", + "properties": [{ "name": "doubleCol", "type": "double" }] + }], + "objects": [ + { "type": "DoubleObject", "value": [-1.001] }, + { "type": "DoubleObject", "value": [0.0] }, + { "type": "DoubleObject", "value": [100.2] } + ], + "tests": [ + ["QueryCount", 1, "DoubleObject", "doubleCol == -1.001"], + ["QueryCount", 1, "DoubleObject", "doubleCol == 0"], + ["QueryCount", 0, "DoubleObject", "1 == doubleCol"], + ["QueryCount", 2, "DoubleObject", "doubleCol != 0"], + ["QueryCount", 2, "DoubleObject", "doubleCol > -1.001"], + ["QueryCount", 3, "DoubleObject", "doubleCol >= -1.001"], + ["QueryCount", 2, "DoubleObject", "doubleCol < 100.2"], + ["QueryCount", 3, "DoubleObject", "doubleCol <= 100.2"], + ["QueryCount", 1, "DoubleObject", "doubleCol > 0x1F"], + ["QueryCount", 1, "DoubleObject", "doubleCol == $0", 100.2], + + ["QueryThrows", "DoubleObject", "doubleCol == 'not a double'"], + ["QueryThrows", "DoubleObject", "doubleCol == true"], + ["QueryThrows", "DoubleObject", "doubleCol == $0", "not a double"], + ["QueryThrows", "DoubleObject", "doubleCol BEGINSWITH 1"], + ["QueryThrows", "DoubleObject", "doubleCol CONTAINS 1"], + ["QueryThrows", "DoubleObject", "doubleCol ENDSWITH 1"] + ] +}, + +"stringTests" : { + "schema" : [{ + "name": "StringObject", + "properties": [{ "name": "stringCol", "type": "string" }] + }], + "objects": [ + { "type": "StringObject", "value": ["A"] }, + { "type": "StringObject", "value": ["a"] }, + { "type": "StringObject", "value": ["a"] }, + { "type": "StringObject", "value": ["C"] }, + { "type": "StringObject", "value": ["c"] }, + { "type": "StringObject", "value": ["abc"] }, + { "type": "StringObject", "value": ["ABC"] }, + { "type": "StringObject", "value": [""] }, + { "type": "StringObject", "value": ["\\\"\\n\\0\\r\\\\'"] } + ], + "tests": [ + ["QueryCount", 2, "StringObject", "stringCol == 'a'"], + ["QueryCount", 1, "StringObject", "'c' == stringCol"], + ["QueryCount", 2, "StringObject", "stringCol == \"a\""], + ["QueryCount", 1, "StringObject", "stringCol=='abc'"], + ["QueryCount", 1, "StringObject", "stringCol == ''"], + ["QueryCount", 8, "StringObject", "stringCol != ''"], + ["QueryCount", 1, "StringObject", "stringCol == \"\\\"\\n\\0\\r\\\\'\""], + ["QueryCount", 3, "StringObject", "stringCol BEGINSWITH 'a'"], + ["QueryCount", 1, "StringObject", "stringCol beginswith 'ab'"], + ["QueryCount", 0, "StringObject", "stringCol BEGINSWITH 'abcd'"], + ["QueryCount", 2, "StringObject", "stringCol BEGINSWITH 'A'"], + ["QueryCount", 2, "StringObject", "stringCol ENDSWITH 'c'"], + ["QueryCount", 1, "StringObject", "stringCol endswith 'bc'"], + ["QueryCount", 9, "StringObject", "stringCol ENDSWITH ''"], + ["QueryCount", 1, "StringObject", "stringCol CONTAINS 'b'"], + ["QueryCount", 2, "StringObject", "stringCol contains 'c'"], + ["QueryCount", 9, "StringObject", "stringCol CONTAINS ''"], + ["QueryCount", 2, "StringObject", "stringCol == $0", "a"], + ["QueryCount", 2, "StringObject", "stringCol ENDSWITH $0", "c"], + + ["QueryThrows", "StringObject", "stringCol == true"], + ["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'"] + ] +}, + +"binaryTests" : { + "schema" : [{ + "name": "BinaryObject", + "properties": [{ "name": "binaryCol", "type": "data" }] + }], + "objects": [ + { "type": "BinaryObject", "value": [[1, 100, 233, 255, 0]] }, + { "type": "BinaryObject", "value": [[1, 100]] }, + { "type": "BinaryObject", "value": [[100]] }, + { "type": "BinaryObject", "value": [[]] }, + { "type": "BinaryObject", "value": [[255, 0]] } + ], + "tests": [ + ["QueryCount", 1, "BinaryObject", "binaryCol == $0", [1, "binaryCol"]], + ["QueryCount", 1, "BinaryObject", "$0 == binaryCol", [2, "binaryCol"]], + ["QueryCount", 4, "BinaryObject", "binaryCol != $0", [0, "binaryCol"]], + ["QueryCount", 1, "BinaryObject", "binaryCol BEGINSWITH $0", [0, "binaryCol"]], + ["QueryCount", 2, "BinaryObject", "binaryCol BEGINSWITH $0", [1, "binaryCol"]], + ["QueryCount", 2, "BinaryObject", "binaryCol ENDSWITH $0", [4, "binaryCol"]], + ["QueryCount", 3, "BinaryObject", "binaryCol CONTAINS $0", [2, "binaryCol"]] + ] +}, + +"objectTests" : { + "schema" : [ + { "name": "IntObject", "properties": [{ "name": "intCol", "type": "int" }] }, + { "name": "LinkObject", "properties": [{ "name": "linkCol", "type": "IntObject" }] } + ], + "objects": [ + { "type": "LinkObject", "value": [[1]] }, + { "type": "LinkObject", "value": [[2]] }, + { "type": "LinkObject", "value": [null] } + ], + "tests": [ + ["QueryCount", 1, "LinkObject", "linkCol == $0", [0, "linkCol"]], + ["QueryCount", 1, "LinkObject", "$0 == linkCol", [1, "linkCol"]], + ["QueryCount", 2, "LinkObject", "linkCol != $0", [0, "linkCol"]], + + ["QueryThrows", "LinkObject", "linkCol > $0", [0, "linkCol"]], + ["QueryThrows", "LinkObject", "intCol = $0", [0, "linkCol"]] + ] +} + +} From 8d13ec1adc22c2975e2ed758fdf59ee0a3b920b4 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Fri, 20 Nov 2015 15:16:35 -0800 Subject: [PATCH 36/66] run parser tests in RealmJSTests --- parser/parser.cpp | 5 +++++ parser/parser.hpp | 3 +++ parser/test.cpp | 14 ++++++++++++-- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/parser/parser.cpp b/parser/parser.cpp index 7d771285..d2f88d8c 100644 --- a/parser/parser.cpp +++ b/parser/parser.cpp @@ -320,6 +320,11 @@ Predicate parse(const std::string &query) return std::move(out_predicate); } +void analyzeGrammer() +{ + analyze(); +} + }} diff --git a/parser/parser.hpp b/parser/parser.hpp index 4792285a..6df4b105 100644 --- a/parser/parser.hpp +++ b/parser/parser.hpp @@ -76,6 +76,9 @@ namespace realm { }; Predicate parse(const std::string &query); + + void analyzeGrammer(); + bool testGrammer(); } } diff --git a/parser/test.cpp b/parser/test.cpp index f18a36ff..973cf672 100644 --- a/parser/test.cpp +++ b/parser/test.cpp @@ -125,20 +125,25 @@ static std::vector invalid_queries = { "a==a &| a==a", "a==a && OR a==a", "a==aORa==a", - "a=1ANDNOT b=2", + //"a=1ANDNOT b=2", "truepredicate &&", "truepredicate & truepredicate", }; -int main( int argc, char ** argv ) +namespace realm { +namespace parser { + +bool testGrammer() { + bool success = true; for (auto &query : valid_queries) { std::cout << "valid query: " << query << std::endl; try { realm::parser::parse(query); } catch (std::exception &ex) { std::cout << "FAILURE - " << ex.what() << std::endl; + success = false; } } @@ -151,6 +156,11 @@ int main( int argc, char ** argv ) continue; } std::cout << "FAILURE - query should throw an exception" << std::endl; + success = false; } + + return success; } +} +} From 2109520913a69ff18f9fadbe1881d2dd6edb38e1 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Mon, 23 Nov 2015 08:47:09 -0800 Subject: [PATCH 37/66] pr feedback --- parser/parser.cpp | 4 ++-- parser/parser.hpp | 14 +++++++++----- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/parser/parser.cpp b/parser/parser.cpp index d2f88d8c..98c81ac5 100644 --- a/parser/parser.cpp +++ b/parser/parser.cpp @@ -151,7 +151,7 @@ template<> struct action< and_ext > // 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 = state.current().cpnd.sub_predicates; + 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 @@ -320,7 +320,7 @@ Predicate parse(const std::string &query) return std::move(out_predicate); } -void analyzeGrammer() +void analyzeGrammar() { analyze(); } diff --git a/parser/parser.hpp b/parser/parser.hpp index 6df4b105..f2119c66 100644 --- a/parser/parser.hpp +++ b/parser/parser.hpp @@ -36,7 +36,8 @@ namespace realm { struct Predicate { - enum class Type { + enum class Type + { Comparison, Or, And, @@ -44,7 +45,8 @@ namespace realm { False } type = Type::And; - enum class Operator { + enum class Operator + { None, Equal, NotEqual, @@ -57,13 +59,15 @@ namespace realm { Contains }; - struct Comparison { + struct Comparison + { Operator op = Operator::None; Expression expr[2]; ~Comparison() {} }; - struct Compound { + struct Compound + { std::vector sub_predicates; }; @@ -77,7 +81,7 @@ namespace realm { Predicate parse(const std::string &query); - void analyzeGrammer(); + void analyzeGrammar(); bool testGrammer(); } } From d5f56540b7b67a0a7d4a122b491b36dbb0f4b5e4 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Mon, 23 Nov 2015 08:56:36 -0800 Subject: [PATCH 38/66] pr fixes --- parser/parser.hpp | 2 +- parser/test.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/parser/parser.hpp b/parser/parser.hpp index f2119c66..f443c5f1 100644 --- a/parser/parser.hpp +++ b/parser/parser.hpp @@ -82,7 +82,7 @@ namespace realm { Predicate parse(const std::string &query); void analyzeGrammar(); - bool testGrammer(); + bool testGrammar(); } } diff --git a/parser/test.cpp b/parser/test.cpp index 973cf672..ce290474 100644 --- a/parser/test.cpp +++ b/parser/test.cpp @@ -134,7 +134,7 @@ static std::vector invalid_queries = { namespace realm { namespace parser { -bool testGrammer() +bool testGrammar() { bool success = true; for (auto &query : valid_queries) { From e05ec4ea8374323b00f7cd3d9120557e7d9fbe61 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Mon, 23 Nov 2015 11:26:50 -0800 Subject: [PATCH 39/66] latest from object store branch --- object_store.cpp | 21 +-- object_store.hpp | 11 +- results.cpp | 326 +++++++++++++++++++++++++++++++++++++++++++---- results.hpp | 159 +++++++++++++++++++---- shared_realm.cpp | 11 +- shared_realm.hpp | 42 +++--- 6 files changed, 485 insertions(+), 85 deletions(-) diff --git a/object_store.cpp b/object_store.cpp index 3e32b98c..3aed85c5 100644 --- a/object_store.cpp +++ b/object_store.cpp @@ -30,6 +30,7 @@ using namespace realm; +namespace { const char * const c_metadataTableName = "metadata"; const char * const c_versionColumnName = "version"; const size_t c_versionColumnIndex = 0; @@ -42,8 +43,8 @@ const size_t c_primaryKeyPropertyNameColumnIndex = 1; const size_t c_zeroRowIndex = 0; -const std::string c_object_table_prefix = "class_"; -const size_t c_object_table_prefix_length = c_object_table_prefix.length(); +const char c_object_table_prefix[] = "class_"; +} const uint64_t ObjectStore::NotVersioned = std::numeric_limits::max(); @@ -119,15 +120,15 @@ void ObjectStore::set_primary_key_for_object(Group *group, StringData object_typ } } -std::string ObjectStore::object_type_for_table_name(const std::string &table_name) { - if (table_name.size() >= c_object_table_prefix_length && table_name.compare(0, c_object_table_prefix_length, c_object_table_prefix) == 0) { - return table_name.substr(c_object_table_prefix_length, table_name.length() - c_object_table_prefix_length); +StringData ObjectStore::object_type_for_table_name(StringData table_name) { + if (table_name.begins_with(c_object_table_prefix)) { + return table_name.substr(sizeof(c_object_table_prefix) - 1); } - return std::string(); + return StringData(); } -std::string ObjectStore::table_name_for_object_type(const std::string &object_type) { - return c_object_table_prefix + object_type; +std::string ObjectStore::table_name_for_object_type(StringData object_type) { + return std::string(c_object_table_prefix) + object_type.data(); } TableRef ObjectStore::table_for_object_type(Group *group, StringData object_type) { @@ -138,7 +139,7 @@ ConstTableRef ObjectStore::table_for_object_type(const Group *group, StringData return group->get_table(table_name_for_object_type(object_type)); } -TableRef ObjectStore::table_for_object_type_create_if_needed(Group *group, const StringData &object_type, bool &created) { +TableRef ObjectStore::table_for_object_type_create_if_needed(Group *group, StringData object_type, bool &created) { return group->get_or_add_table(table_name_for_object_type(object_type), &created); } @@ -494,7 +495,7 @@ void ObjectStore::validate_primary_column_uniqueness(const Group *group, Schema } } -void ObjectStore::delete_data_for_object(Group *group, const StringData &object_type) { +void ObjectStore::delete_data_for_object(Group *group, StringData object_type) { TableRef table = table_for_object_type(group, object_type); if (table) { group->remove_table(table->get_index_in_group()); diff --git a/object_store.hpp b/object_store.hpp index e038604c..d41ec3fd 100644 --- a/object_store.hpp +++ b/object_store.hpp @@ -19,7 +19,6 @@ #ifndef REALM_OBJECT_STORE_HPP #define REALM_OBJECT_STORE_HPP -#include "schema.hpp" #include "object_schema.hpp" #include "property.hpp" @@ -30,6 +29,7 @@ namespace realm { class ObjectSchemaValidationException; + class Schema; class ObjectStore { public: @@ -68,11 +68,14 @@ namespace realm { static Schema schema_from_group(const Group *group); // deletes the table for the given type - static void delete_data_for_object(Group *group, const StringData &object_type); + static void delete_data_for_object(Group *group, StringData object_type); // indicates if this group contains any objects static bool is_empty(const Group *group); + static std::string table_name_for_object_type(StringData class_name); + static StringData object_type_for_table_name(StringData table_name); + private: // set a new schema version static void set_schema_version(Group *group, uint64_t version); @@ -102,9 +105,7 @@ namespace realm { // must be in write transaction to set static void set_primary_key_for_object(Group *group, StringData object_type, StringData primary_key); - static TableRef table_for_object_type_create_if_needed(Group *group, const StringData &object_type, bool &created); - static std::string table_name_for_object_type(const std::string &class_name); - static std::string object_type_for_table_name(const std::string &table_name); + static TableRef table_for_object_type_create_if_needed(Group *group, StringData object_type, bool &created); // returns if any indexes were changed static bool update_indexes(Group *group, Schema &schema); diff --git a/results.cpp b/results.cpp index 1977e5be..f361e6f5 100644 --- a/results.cpp +++ b/results.cpp @@ -3,42 +3,318 @@ */ #include "results.hpp" -#import + +#include using namespace realm; -Results::Results(SharedRealm &r, ObjectSchema &o, Query q, SortOrder s) : - realm(r), object_schema(o), backing_query(q), table_view(backing_query.find_all()) +#ifdef __has_cpp_attribute +#define REALM_HAS_CCP_ATTRIBUTE(attr) __has_cpp_attribute(attr) +#else +#define REALM_HAS_CCP_ATTRIBUTE(attr) 0 +#endif + +#if REALM_HAS_CCP_ATTRIBUTE(clang::fallthrough) +#define REALM_FALLTHROUGH [[clang::fallthrough]] +#else +#define REALM_FALLTHROUGH +#endif + +Results::Results(SharedRealm r, Query q, SortOrder s) +: m_realm(std::move(r)) +, m_query(std::move(q)) +, m_table(m_query.get_table().get()) +, m_sort(std::move(s)) +, m_mode(Mode::Query) { - setSort(std::move(s)); +} + +Results::Results(SharedRealm r, Table& table) +: m_realm(std::move(r)) +, m_table(&table) +, m_mode(Mode::Table) +{ +} + +void Results::validate_read() const +{ + if (m_realm) + m_realm->verify_thread(); + if (m_table && !m_table->is_attached()) + throw InvalidatedException(); +} + +void Results::validate_write() const +{ + validate_read(); + if (!m_realm || !m_realm->is_in_transaction()) + throw InvalidTransactionException("Must be in a write transaction"); } size_t Results::size() { - verify_attached(); - return table_view.size(); -} - -void Results::setSort(SortOrder s) -{ - sort_order = std::make_unique(std::move(s)); - table_view.sort(sort_order->columnIndices, sort_order->ascending); -} - -Row Results::get(std::size_t row_ndx) -{ - verify_attached(); - if (row_ndx >= table_view.size()) { - throw std::out_of_range(std::string("Index ") + std::to_string(row_ndx) + " is outside of range 0..." + - std::to_string(table_view.size()) + "."); + validate_read(); + switch (m_mode) { + case Mode::Empty: return 0; + case Mode::Table: return m_table->size(); + case Mode::Query: return m_query.count(); + case Mode::TableView: + update_tableview(); + return m_table_view.size(); } - return table_view.get(row_ndx); + REALM_UNREACHABLE(); } -void Results::verify_attached() +RowExpr Results::get(size_t row_ndx) { - if (!table_view.is_attached()) { - throw std::runtime_error("Tableview is not attached"); + validate_read(); + switch (m_mode) { + case Mode::Empty: break; + case Mode::Table: + if (row_ndx < m_table->size()) + return m_table->get(row_ndx); + break; + case Mode::Query: + case Mode::TableView: + update_tableview(); + if (row_ndx < m_table_view.size()) + return m_table_view.get(row_ndx); + break; } - table_view.sync_if_needed(); + + throw OutOfBoundsIndexException{row_ndx, size()}; +} + +util::Optional Results::first() +{ + validate_read(); + switch (m_mode) { + case Mode::Empty: + return none; + case Mode::Table: + return m_table->size() == 0 ? util::none : util::make_optional(m_table->front()); + case Mode::Query: + update_tableview(); + REALM_FALLTHROUGH; + case Mode::TableView: + return m_table_view.size() == 0 ? util::none : util::make_optional(m_table_view.front()); + } + REALM_UNREACHABLE(); +} + +util::Optional Results::last() +{ + validate_read(); + switch (m_mode) { + case Mode::Empty: + return none; + case Mode::Table: + return m_table->size() == 0 ? util::none : util::make_optional(m_table->back()); + case Mode::Query: + update_tableview(); + REALM_FALLTHROUGH; + case Mode::TableView: + return m_table_view.size() == 0 ? util::none : util::make_optional(m_table_view.back()); + } + REALM_UNREACHABLE(); +} + +void Results::update_tableview() +{ + validate_read(); + switch (m_mode) { + case Mode::Empty: + case Mode::Table: + return; + case Mode::Query: + m_table_view = m_query.find_all(); + if (m_sort) { + m_table_view.sort(m_sort.columnIndices, m_sort.ascending); + } + m_mode = Mode::TableView; + break; + case Mode::TableView: + m_table_view.sync_if_needed(); + break; + } +} + +size_t Results::index_of(Row const& row) +{ + validate_read(); + if (!row) { + throw DetatchedAccessorException{}; + } + if (m_table && row.get_table() != m_table) { + throw IncorrectTableException{ + ObjectStore::object_type_for_table_name(m_table->get_name()), + ObjectStore::object_type_for_table_name(row.get_table()->get_name())}; + } + return index_of(row.get_index()); +} + +size_t Results::index_of(size_t row_ndx) +{ + validate_read(); + switch (m_mode) { + case Mode::Empty: + return not_found; + case Mode::Table: + return row_ndx; + case Mode::Query: + if (!m_sort) + return m_query.count(row_ndx, row_ndx + 1) ? m_query.count(0, row_ndx) : not_found; + REALM_FALLTHROUGH; + case Mode::TableView: + update_tableview(); + return m_table_view.find_by_source_ndx(row_ndx); + } + REALM_UNREACHABLE(); +} + +template +util::Optional Results::aggregate(size_t column, bool return_none_for_empty, + Int agg_int, Float agg_float, + Double agg_double, DateTime agg_datetime) +{ + validate_read(); + if (!m_table) + return none; + if (column > m_table->get_column_count()) + throw OutOfBoundsIndexException{column, m_table->get_column_count()}; + + auto do_agg = [&](auto const& getter) -> util::Optional { + switch (m_mode) { + case Mode::Empty: + return none; + case Mode::Table: + if (return_none_for_empty && m_table->size() == 0) + return none; + return util::Optional(getter(*m_table)); + case Mode::Query: + case Mode::TableView: + this->update_tableview(); + if (return_none_for_empty && m_table_view.size() == 0) + return none; + return util::Optional(getter(m_table_view)); + } + REALM_UNREACHABLE(); + }; + + switch (m_table->get_column_type(column)) + { + case type_DateTime: return do_agg(agg_datetime); + case type_Double: return do_agg(agg_double); + case type_Float: return do_agg(agg_float); + case type_Int: return do_agg(agg_int); + default: + throw UnsupportedColumnTypeException{column, m_table}; + } +} + +util::Optional Results::max(size_t column) +{ + return aggregate(column, true, + [=](auto const& table) { return table.maximum_int(column); }, + [=](auto const& table) { return table.maximum_float(column); }, + [=](auto const& table) { return table.maximum_double(column); }, + [=](auto const& table) { return table.maximum_datetime(column); }); +} + +util::Optional Results::min(size_t column) +{ + return aggregate(column, true, + [=](auto const& table) { return table.minimum_int(column); }, + [=](auto const& table) { return table.minimum_float(column); }, + [=](auto const& table) { return table.minimum_double(column); }, + [=](auto const& table) { return table.minimum_datetime(column); }); +} + +util::Optional Results::sum(size_t column) +{ + return aggregate(column, false, + [=](auto const& table) { return table.sum_int(column); }, + [=](auto const& table) { return table.sum_float(column); }, + [=](auto const& table) { return table.sum_double(column); }, + [=](auto const&) -> util::None { throw UnsupportedColumnTypeException{column, m_table}; }); +} + +util::Optional Results::average(size_t column) +{ + return aggregate(column, true, + [=](auto const& table) { return table.average_int(column); }, + [=](auto const& table) { return table.average_float(column); }, + [=](auto const& table) { return table.average_double(column); }, + [=](auto const&) -> util::None { throw UnsupportedColumnTypeException{column, m_table}; }); +} + +void Results::clear() +{ + switch (m_mode) { + case Mode::Empty: + return; + case Mode::Table: + validate_write(); + m_table->clear(); + break; + case Mode::Query: + // Not using Query:remove() because building the tableview and + // clearing it is actually significantly faster + case Mode::TableView: + validate_write(); + update_tableview(); + m_table_view.clear(); + break; + } +} + +Query Results::get_query() const +{ + validate_read(); + switch (m_mode) { + case Mode::Empty: + case Mode::Query: + case Mode::TableView: + return m_query; + case Mode::Table: + return m_table->where(); + } + REALM_UNREACHABLE(); +} + +TableView Results::get_tableview() +{ + validate_read(); + switch (m_mode) { + case Mode::Empty: + return {}; + case Mode::Query: + case Mode::TableView: + update_tableview(); + return m_table_view; + case Mode::Table: + return m_table->where().find_all(); + } + REALM_UNREACHABLE(); +} + +StringData Results::get_object_type() const noexcept +{ + return ObjectStore::object_type_for_table_name(m_table->get_name()); +} + +Results Results::sort(realm::SortOrder&& sort) const +{ + return Results(m_realm, get_query(), std::move(sort)); +} + +Results Results::filter(Query&& q) const +{ + return Results(m_realm, get_query().and_query(std::move(q)), get_sort()); +} + +Results::UnsupportedColumnTypeException::UnsupportedColumnTypeException(size_t column, const Table* table) { + column_index = column; + column_name = table->get_column_name(column); + column_type = table->get_column_type(column); } diff --git a/results.hpp b/results.hpp index fff37124..bf9761a3 100644 --- a/results.hpp +++ b/results.hpp @@ -5,35 +5,150 @@ #ifndef REALM_RESULTS_HPP #define REALM_RESULTS_HPP -#import "shared_realm.hpp" -#import +#include "shared_realm.hpp" + +#include +#include +#include namespace realm { - struct SortOrder { - std::vector columnIndices; - std::vector ascending; +template class BasicRowExpr; +using RowExpr = BasicRowExpr
; +class Mixed; - explicit operator bool() const { - return !columnIndices.empty(); - } +struct SortOrder { + std::vector columnIndices; + std::vector ascending; + + explicit operator bool() const + { + return !columnIndices.empty(); + } +}; + +class Results { +public: + // Results can be either be backed by nothing, a thin wrapper around a table, + // or a wrapper around a query and a sort order which creates and updates + // the tableview as needed + Results() = default; + Results(SharedRealm r, Table& table); + Results(SharedRealm r, Query q, SortOrder s = {}); + + // Results is copyable and moveable + Results(Results const&) = default; + Results(Results&&) = default; + Results& operator=(Results const&) = default; + Results& operator=(Results&&) = default; + + // Get a query which will match the same rows as is contained in this Results + // Returned query will not be valid if the current mode is Empty + Query get_query() const; + + // Get the currently applied sort order for this Results + SortOrder const& get_sort() const noexcept { return m_sort; } + + // Get a tableview containing the same rows as this Results + TableView get_tableview(); + + // Get the object type which will be returned by get() + StringData get_object_type() const noexcept; + + // Get the size of this results + // Can be either O(1) or O(N) depending on the state of things + size_t size(); + + // Get the row accessor for the given index + // Throws OutOfBoundsIndexException if index >= size() + RowExpr get(size_t index); + + // Get a row accessor for the first/last row, or none if the results are empty + // More efficient than calling size()+get() + util::Optional first(); + util::Optional last(); + + // Get the first index of the given row in this results, or not_found + // Throws DetachedAccessorException if row is not attached + // Throws IncorrectTableException if row belongs to a different table + size_t index_of(size_t row_ndx); + size_t index_of(Row const& row); + + // Delete all of the rows in this Results from the Realm + // size() will always be zero afterwards + // Throws InvalidTransactionException if not in a write transaction + void clear(); + + // Create a new Results by further filtering or sorting this Results + Results filter(Query&& q) const; + Results sort(SortOrder&& sort) const; + + // Get the min/max/average/sum of the given column + // All but sum() returns none when there are zero matching rows + // sum() returns 0, except for when it returns none + // Throws UnsupportedColumnTypeException for sum/average on datetime or non-numeric column + // Throws OutOfBoundsIndexException for an out-of-bounds column + util::Optional max(size_t column); + util::Optional min(size_t column); + util::Optional average(size_t column); + util::Optional sum(size_t column); + + enum class Mode { + Empty, // Backed by nothing (for missing tables) + Table, // Backed directly by a Table + Query, // Backed by a query that has not yet been turned into a TableView + TableView // Backed by a TableView created from a Query + }; + // Get the currrent mode of the Results + // Ideally this would not be public but it's needed for some KVO stuff + Mode get_mode() const { return m_mode; } + + // The Results object has been invalidated (due to the Realm being invalidated) + // All non-noexcept functions can throw this + struct InvalidatedException {}; + + // The input index parameter was out of bounds + struct OutOfBoundsIndexException { + size_t requested; + size_t valid_count; }; - static SortOrder s_defaultSort = {{}, {}}; + // The input Row object is not attached + struct DetatchedAccessorException { }; - struct Results { - Results(SharedRealm &r, ObjectSchema &o, Query q, SortOrder s = s_defaultSort); - size_t size(); - Row get(std::size_t row_ndx); - void verify_attached(); - - SharedRealm realm; - ObjectSchema &object_schema; - Query backing_query; - TableView table_view; - std::unique_ptr sort_order; - - void setSort(SortOrder s); + // The input Row object belongs to a different table + struct IncorrectTableException { + StringData expected; + StringData actual; }; + + // The requested aggregate operation is not supported for the column type + struct UnsupportedColumnTypeException { + size_t column_index; + StringData column_name; + DataType column_type; + + UnsupportedColumnTypeException(size_t column, const Table* table); + }; + +private: + SharedRealm m_realm; + Query m_query; + TableView m_table_view; + Table* m_table = nullptr; + SortOrder m_sort; + + Mode m_mode = Mode::Empty; + + void validate_read() const; + void validate_write() const; + + void update_tableview(); + + template + util::Optional aggregate(size_t column, bool return_none_for_empty, + Int agg_int, Float agg_float, + Double agg_double, DateTime agg_datetime); +}; } #endif /* REALM_RESULTS_HPP */ diff --git a/shared_realm.cpp b/shared_realm.cpp index 9c0a4e1e..28bb81b6 100644 --- a/shared_realm.cpp +++ b/shared_realm.cpp @@ -47,6 +47,8 @@ Realm::Config::Config(const Config& c) } } +Realm::Config::Config() = default; +Realm::Config::Config(Config&&) = default; Realm::Config::~Config() = default; Realm::Config& Realm::Config::operator=(realm::Realm::Config const& c) @@ -237,7 +239,14 @@ static void check_read_write(Realm *realm) void Realm::verify_thread() const { if (m_thread_id != std::this_thread::get_id()) { - throw IncorrectThreadException("Realm accessed from incorrect thread."); + throw IncorrectThreadException(); + } +} + +void Realm::verify_in_write() const +{ + if (!is_in_transaction()) { + throw InvalidTransactionException("Cannot modify persisted objects outside of a write transaction."); } } diff --git a/shared_realm.hpp b/shared_realm.hpp index 81b14bf7..3a31d878 100644 --- a/shared_realm.hpp +++ b/shared_realm.hpp @@ -19,7 +19,6 @@ #ifndef REALM_REALM_HPP #define REALM_REALM_HPP -#include #include #include #include @@ -56,8 +55,8 @@ namespace realm { MigrationFunction migration_function; - Config() = default; - Config(Config&&) = default; + Config(); + Config(Config&&); Config(const Config& c); ~Config(); @@ -100,6 +99,11 @@ namespace realm { std::thread::id thread_id() const { return m_thread_id; } void verify_thread() const; + void verify_in_write() const; + + // Close this Realm and remove it from the cache. Continuing to use a + // Realm after closing it will produce undefined behavior. + void close(); // Close this Realm and remove it from the cache. Continuing to use a // Realm after closing it will produce undefined behavior. @@ -145,11 +149,9 @@ namespace realm { std::mutex m_mutex; }; - class RealmFileException : public std::runtime_error - { - public: - enum class Kind - { + class RealmFileException : public std::runtime_error { + public: + enum class Kind { /** Thrown for any I/O related exception scenarios when a realm is opened. */ AccessError, /** Thrown if the user does not have permission to open or create @@ -169,32 +171,28 @@ namespace realm { Kind kind() const { return m_kind; } const std::string& path() const { return m_path; } - private: + private: Kind m_kind; std::string m_path; }; - class MismatchedConfigException : public std::runtime_error - { - public: + class MismatchedConfigException : public std::runtime_error { + public: MismatchedConfigException(std::string message) : std::runtime_error(message) {} }; - class InvalidTransactionException : public std::runtime_error - { - public: + class InvalidTransactionException : public std::runtime_error { + public: InvalidTransactionException(std::string message) : std::runtime_error(message) {} }; - class IncorrectThreadException : public std::runtime_error - { - public: - IncorrectThreadException(std::string message) : std::runtime_error(message) {} + class IncorrectThreadException : public std::runtime_error { + public: + IncorrectThreadException() : std::runtime_error("Realm accessed from incorrect thread.") {} }; - class UnitializedRealmException : public std::runtime_error - { - public: + class UnitializedRealmException : public std::runtime_error { + public: UnitializedRealmException(std::string message) : std::runtime_error(message) {} }; } From fa0ba53579ded98d0d977929981040da961d04a7 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Mon, 23 Nov 2015 19:00:31 -0800 Subject: [PATCH 40/66] fix for reload in example --- shared_realm.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/shared_realm.cpp b/shared_realm.cpp index 9c0a4e1e..2b2f61c3 100644 --- a/shared_realm.cpp +++ b/shared_realm.cpp @@ -376,8 +376,6 @@ uint64_t Realm::get_schema_version(const realm::Realm::Config &config) void Realm::close() { - invalidate(); - if (m_notifier) { m_notifier->remove_realm(this); } From 054f185c1b6f25d782caf26561025261f25ad297 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Tue, 24 Nov 2015 10:39:42 -0800 Subject: [PATCH 41/66] compount parser tests --- parser/queryTests.json | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/parser/queryTests.json b/parser/queryTests.json index f9a5f56f..75daa28f 100644 --- a/parser/queryTests.json +++ b/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"] + ] } } From 4b9af98a8158add846150102a3d6395477d1d071 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Tue, 24 Nov 2015 11:18:03 -0800 Subject: [PATCH 42/66] fix for mixed && and || queries --- parser/parser.cpp | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/parser/parser.cpp b/parser/parser.cpp index 98c81ac5..9765eeb7 100644 --- a/parser/parser.cpp +++ b/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 295b378e7ffa60b3962c3aaec16e1a9c4a082ebe Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Wed, 25 Nov 2015 12:49:31 -0800 Subject: [PATCH 43/66] process compound operators in the correct order --- parser/parser.cpp | 173 +++++++++++++++++++++-------------------- parser/parser.hpp | 4 +- parser/queryTests.json | 3 +- 3 files changed, 91 insertions(+), 89 deletions(-) diff --git a/parser/parser.cpp b/parser/parser.cpp index 9765eeb7..b9b0373d 100644 --- a/parser/parser.cpp +++ b/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/parser/parser.hpp b/parser/parser.hpp index f443c5f1..4081f3d3 100644 --- a/parser/parser.hpp +++ b/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/parser/queryTests.json b/parser/queryTests.json index 75daa28f..518759cc 100644 --- a/parser/queryTests.json +++ b/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 a707a728cded9bf765bd6e330433a88a65ddf427 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Wed, 25 Nov 2015 12:54:13 -0800 Subject: [PATCH 44/66] add a few more tests --- parser/queryTests.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/parser/queryTests.json b/parser/queryTests.json index 518759cc..fb280f19 100644 --- a/parser/queryTests.json +++ b/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 e89259c746e8f1060ef7990ba7bd74388b695541 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Wed, 25 Nov 2015 12:57:56 -0800 Subject: [PATCH 45/66] more tests --- parser/queryTests.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/parser/queryTests.json b/parser/queryTests.json index fb280f19..15f7fefa 100644 --- a/parser/queryTests.json +++ b/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 be2a3fab47e79ad2b418f0f4c6229cf6d8c5b3d0 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Wed, 25 Nov 2015 18:32:13 -0800 Subject: [PATCH 46/66] don't merge predicate to negated and group --- parser/parser.cpp | 5 ++--- parser/queryTests.json | 3 ++- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/parser/parser.cpp b/parser/parser.cpp index b9b0373d..f478d1c5 100644 --- a/parser/parser.cpp +++ b/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/parser/queryTests.json b/parser/queryTests.json index 15f7fefa..630694e8 100644 --- a/parser/queryTests.json +++ b/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"] ] } From 6ac6f3989408b54c6baefc19d1badaa35bfde4e2 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Wed, 25 Nov 2015 18:34:36 -0800 Subject: [PATCH 47/66] use current_group() helper --- parser/parser.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/parser/parser.cpp b/parser/parser.cpp index f478d1c5..2753fbd8 100644 --- a/parser/parser.cpp +++ b/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 1faf3d21d4502ef7370a3495d9498373cc0eab86 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Wed, 25 Nov 2015 19:10:59 -0800 Subject: [PATCH 48/66] make None the first enum type --- parser/parser.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parser/parser.hpp b/parser/parser.hpp index 4081f3d3..377eda5d 100644 --- a/parser/parser.hpp +++ b/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) {} From c9405da94fc849d53e3390964acbace2b956e4ef Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Wed, 25 Nov 2015 19:57:15 -0800 Subject: [PATCH 49/66] integrate new results class --- object_accessor.hpp | 1 + results.cpp | 29 +++++++++++++---------------- results.hpp | 41 +++++++++++++++++++++++++++++------------ shared_realm.hpp | 4 ---- 4 files changed, 43 insertions(+), 32 deletions(-) diff --git a/object_accessor.hpp b/object_accessor.hpp index dfae7c16..ffa3edb6 100644 --- a/object_accessor.hpp +++ b/object_accessor.hpp @@ -7,6 +7,7 @@ #include #include "shared_realm.hpp" +#include "schema.hpp" #include "list.hpp" namespace realm { diff --git a/results.cpp b/results.cpp index f361e6f5..2f466476 100644 --- a/results.cpp +++ b/results.cpp @@ -20,19 +20,21 @@ using namespace realm; #define REALM_FALLTHROUGH #endif -Results::Results(SharedRealm r, Query q, SortOrder s) +Results::Results(SharedRealm r, const ObjectSchema &o, Query q, SortOrder s) : m_realm(std::move(r)) , m_query(std::move(q)) , m_table(m_query.get_table().get()) , m_sort(std::move(s)) , m_mode(Mode::Query) +, object_schema(o) { } -Results::Results(SharedRealm r, Table& table) +Results::Results(SharedRealm r, const ObjectSchema &o, Table& table) : m_realm(std::move(r)) , m_table(&table) , m_mode(Mode::Table) +, object_schema(o) { } @@ -146,9 +148,9 @@ size_t Results::index_of(Row const& row) throw DetatchedAccessorException{}; } if (m_table && row.get_table() != m_table) { - throw IncorrectTableException{ - ObjectStore::object_type_for_table_name(m_table->get_name()), - ObjectStore::object_type_for_table_name(row.get_table()->get_name())}; + throw IncorrectTableException(object_schema.name, + ObjectStore::object_type_for_table_name(row.get_table()->get_name()), + "Attempting to get the index of a Row of the wrong type"); } return index_of(row.get_index()); } @@ -298,23 +300,18 @@ TableView Results::get_tableview() REALM_UNREACHABLE(); } -StringData Results::get_object_type() const noexcept -{ - return ObjectStore::object_type_for_table_name(m_table->get_name()); -} - Results Results::sort(realm::SortOrder&& sort) const { - return Results(m_realm, get_query(), std::move(sort)); + return Results(m_realm, object_schema, get_query(), std::move(sort)); } Results Results::filter(Query&& q) const { - return Results(m_realm, get_query().and_query(std::move(q)), get_sort()); + return Results(m_realm, object_schema, get_query().and_query(std::move(q)), get_sort()); } -Results::UnsupportedColumnTypeException::UnsupportedColumnTypeException(size_t column, const Table* table) { - column_index = column; - column_name = table->get_column_name(column); - column_type = table->get_column_type(column); +Results::UnsupportedColumnTypeException::UnsupportedColumnTypeException(size_t column, const Table* table) : + column_index(column), column_name(table->get_column_name(column)), column_type(table->get_column_type(column)), + std::runtime_error((std::string)"Operation not supported on '" + table->get_column_name(column).data() + "' columns") +{ } diff --git a/results.hpp b/results.hpp index bf9761a3..53ee6815 100644 --- a/results.hpp +++ b/results.hpp @@ -32,8 +32,8 @@ public: // or a wrapper around a query and a sort order which creates and updates // the tableview as needed Results() = default; - Results(SharedRealm r, Table& table); - Results(SharedRealm r, Query q, SortOrder s = {}); + Results(SharedRealm r, const ObjectSchema &o, Table& table); + Results(SharedRealm r, const ObjectSchema &o, Query q, SortOrder s = {}); // Results is copyable and moveable Results(Results const&) = default; @@ -41,6 +41,12 @@ public: Results& operator=(Results const&) = default; Results& operator=(Results&&) = default; + // Get the Realm + SharedRealm get_realm() const { return m_realm; } + + // Object schema describing the vendored object type + ObjectSchema object_schema; + // Get a query which will match the same rows as is contained in this Results // Returned query will not be valid if the current mode is Empty Query get_query() const; @@ -52,7 +58,7 @@ public: TableView get_tableview(); // Get the object type which will be returned by get() - StringData get_object_type() const noexcept; + StringData get_object_type() const noexcept { return object_schema.name; } // Get the size of this results // Can be either O(1) or O(N) depending on the state of things @@ -104,25 +110,36 @@ public: // The Results object has been invalidated (due to the Realm being invalidated) // All non-noexcept functions can throw this - struct InvalidatedException {}; + struct InvalidatedException : public std::runtime_error + { + InvalidatedException() : std::runtime_error("Access to invalidated Results objects") {} + }; // The input index parameter was out of bounds - struct OutOfBoundsIndexException { - size_t requested; - size_t valid_count; + struct OutOfBoundsIndexException : public std::out_of_range + { + OutOfBoundsIndexException(size_t r, size_t c) : requested(r), valid_count(c), + std::out_of_range((std::string)"Requested index " + std::to_string(r) + + " greater than max " + std::to_string(c)) {} + const size_t requested; + const size_t valid_count; }; // The input Row object is not attached - struct DetatchedAccessorException { }; + struct DetatchedAccessorException : public std::runtime_error { + DetatchedAccessorException() : std::runtime_error("Atempting to access an invalid object") {} + }; // The input Row object belongs to a different table - struct IncorrectTableException { - StringData expected; - StringData actual; + struct IncorrectTableException : public std::runtime_error { + IncorrectTableException(StringData e, StringData a, const std::string &error) : + expected(e), actual(a), std::runtime_error(error) {} + const StringData expected; + const StringData actual; }; // The requested aggregate operation is not supported for the column type - struct UnsupportedColumnTypeException { + struct UnsupportedColumnTypeException : public std::runtime_error { size_t column_index; StringData column_name; DataType column_type; diff --git a/shared_realm.hpp b/shared_realm.hpp index 3a31d878..0503282a 100644 --- a/shared_realm.hpp +++ b/shared_realm.hpp @@ -105,10 +105,6 @@ namespace realm { // Realm after closing it will produce undefined behavior. void close(); - // Close this Realm and remove it from the cache. Continuing to use a - // Realm after closing it will produce undefined behavior. - void close(); - ~Realm(); private: From 08688753661d700c099282fac845dd8533286b36 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Fri, 27 Nov 2015 18:26:58 -0800 Subject: [PATCH 50/66] tests for keypath queries --- parser/queryTests.json | 45 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/parser/queryTests.json b/parser/queryTests.json index 630694e8..bc1d4bc3 100644 --- a/parser/queryTests.json +++ b/parser/queryTests.json @@ -229,8 +229,12 @@ "objectTests" : { "schema" : [ - { "name": "IntObject", "properties": [{ "name": "intCol", "type": "int" }] }, - { "name": "LinkObject", "properties": [{ "name": "linkCol", "type": "IntObject" }] } + { "name": "IntObject", "properties": [ + { "name": "intCol", "type": "int" } + ]}, + { "name": "LinkObject", "properties": [ + { "name": "linkCol", "type": "object", "objectType": "IntObject" } + ]} ], "objects": [ { "type": "LinkObject", "value": [[1]] }, @@ -276,6 +280,43 @@ ["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"] ] +}, + +"keyPathTests" : { + "schema" : [ + { + "name": "BasicTypesObject", + "properties": [ + { "name": "intCol", "type": "int" }, + { "name": "floatCol", "type": "float" }, + { "name": "doubleCol", "type": "double" }, + { "name": "stringCol", "type": "string" }, + { "name": "dateCol", "type": "date" }, + { "name": "dataCol", "type": "data" } + ] + }, + { + "name": "LinkTypesObject", + "primaryKey": "primaryKey", + "properties": [ + { "name": "primaryKey", "type": "int" }, + { "name": "basicLink", "type": "object", "objectType": "BasicTypesObject" }, + { "name": "linkLink", "type": "object", "objectType": "LinkTypesObject" } + ] + }], + "objects": [ + { "type": "LinkTypesObject", "value": [0, [1, 0.1, 0.001, "1", 1, [1, 10, 100]], null] }, + { "type": "LinkTypesObject", "value": [1, null, [2, [1, 0.1, 0.001, "1", 1, [1, 10, 100]], null]] }, + { "type": "LinkTypesObject", "value": [3, null, [4, [2, 0.2, 0.002, "2", 2, [2, 20, 200]], null]] } + ], + "tests": [ + ["ObjectSet", [0, 2], "LinkTypesObject", "basicLink.intCol == 1"], + ["ObjectSet", [1], "LinkTypesObject", "linkLink.basicLink.intCol == 1"], + ["ObjectSet", [1, 3], "LinkTypesObject", "linkLink.basicLink.intCol > 0"], + ["ObjectSet", [0, 2], "LinkTypesObject", "basicLink.floatCol == 0.1"], + ["ObjectSet", [1], "LinkTypesObject", "linkLink.basicLink.floatCol == 0.1"], + ["ObjectSet", [1, 3], "LinkTypesObject", "linkLink.basicLink.floatCol > 0"] + ] } } From 253a572ca741bb88d4fc33b0a8a4d76a4487b1df Mon Sep 17 00:00:00 2001 From: Scott Kyle Date: Wed, 16 Dec 2015 13:04:43 -0800 Subject: [PATCH 51/66] Fix crash caused by accessing invalid ObjectSchema These references would eventually become invalid. The quickest fix is for them to no longer be references. The longer-term fix might be to only store the object type and dynamically retrieve the ObjectSchema only when necessary. Fixes #181 --- list.hpp | 2 +- object_accessor.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/list.hpp b/list.hpp index fdde4c62..934c56ce 100644 --- a/list.hpp +++ b/list.hpp @@ -27,7 +27,7 @@ namespace realm { public: List(SharedRealm &r, const ObjectSchema &s, LinkViewRef l) : m_realm(r), object_schema(s), m_link_view(l) {} - const ObjectSchema &object_schema; + const ObjectSchema object_schema; SharedRealm realm() { return m_realm; } size_t size(); diff --git a/object_accessor.hpp b/object_accessor.hpp index ffa3edb6..2f52619c 100644 --- a/object_accessor.hpp +++ b/object_accessor.hpp @@ -27,7 +27,7 @@ namespace realm { template static inline Object create(ContextType ctx, SharedRealm realm, ObjectSchema &object_schema, ValueType value, bool try_update); - const ObjectSchema &object_schema; + const ObjectSchema object_schema; SharedRealm realm() { return m_realm; } Row row() { return m_row; } From dc67be30fc597298a9837a2cad10e1fb715a1010 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Thu, 17 Dec 2015 15:59:34 -0800 Subject: [PATCH 52/66] Schema stored in Realm::Config should be const --- object_accessor.hpp | 6 +++--- parser/query_builder.cpp | 12 ++++++------ parser/query_builder.hpp | 2 +- shared_realm.cpp | 14 ++++++-------- shared_realm.hpp | 4 ++-- 5 files changed, 18 insertions(+), 20 deletions(-) diff --git a/object_accessor.hpp b/object_accessor.hpp index 2f52619c..5d290d43 100644 --- a/object_accessor.hpp +++ b/object_accessor.hpp @@ -25,7 +25,7 @@ namespace realm { // create an Object from a native representation template - static inline Object create(ContextType ctx, SharedRealm realm, ObjectSchema &object_schema, ValueType value, bool try_update); + static inline Object create(ContextType ctx, SharedRealm realm, const ObjectSchema &object_schema, ValueType value, bool try_update); const ObjectSchema object_schema; SharedRealm realm() { return m_realm; } @@ -243,7 +243,7 @@ namespace realm { } template - inline Object Object::create(ContextType ctx, SharedRealm realm, ObjectSchema &object_schema, ValueType value, bool try_update) + inline Object Object::create(ContextType ctx, SharedRealm realm, const ObjectSchema &object_schema, ValueType value, bool try_update) { using Accessor = NativeAccessor; @@ -283,7 +283,7 @@ namespace realm { // populate Object object(realm, object_schema, table->get(row_index)); - for (Property &prop : object_schema.properties) { + for (const Property &prop : object_schema.properties) { if (created || !prop.is_primary) { if (Accessor::dict_has_value_for_key(ctx, value, prop.name)) { object.set_property_value_impl(ctx, prop, Accessor::dict_value_for_key(ctx, value, prop.name), try_update); diff --git a/parser/query_builder.cpp b/parser/query_builder.cpp index d7f26e07..6f74f3b2 100644 --- a/parser/query_builder.cpp +++ b/parser/query_builder.cpp @@ -66,11 +66,11 @@ KeyPath key_path_from_string(const std::string &s) { struct PropertyExpression { - Property *prop = nullptr; + const Property *prop = nullptr; std::vector indexes; std::function
table_getter; - PropertyExpression(Query &query, Schema &schema, Schema::iterator desc, const std::string &key_path_string) + PropertyExpression(Query &query, const Schema &schema, Schema::const_iterator desc, const std::string &key_path_string) { KeyPath key_path = key_path_from_string(key_path_string); for (size_t index = 0; index < key_path.size(); index++) { @@ -375,7 +375,7 @@ auto value_of_type_for_query(TableGetter&& tables, Value&& value, Arguments &arg } template -void do_add_comparison_to_query(Query &query, Schema &schema, ObjectSchema &object_schema, Predicate::Operator op, +void do_add_comparison_to_query(Query &query, const Schema &schema, const ObjectSchema &object_schema, Predicate::Operator op, PropertyExpression &expr, A &lhs, B &rhs, Arguments &args) { auto type = expr.prop->type; @@ -418,7 +418,7 @@ void do_add_comparison_to_query(Query &query, Schema &schema, ObjectSchema &obje } } -void add_comparison_to_query(Query &query, Predicate &pred, Arguments &args, Schema &schema, const std::string &type) +void add_comparison_to_query(Query &query, Predicate &pred, Arguments &args, const Schema &schema, const std::string &type) { Predicate::Comparison &cmpr = pred.cmpr; auto t0 = cmpr.expr[0].type, t1 = cmpr.expr[1].type; @@ -436,7 +436,7 @@ void add_comparison_to_query(Query &query, Predicate &pred, Arguments &args, Sch } } -void update_query_with_predicate(Query &query, Predicate &pred, Arguments &arguments, Schema &schema, const std::string &type) +void update_query_with_predicate(Query &query, Predicate &pred, Arguments &arguments, const Schema &schema, const std::string &type) { if (pred.negate) { query.Not(); @@ -484,7 +484,7 @@ void update_query_with_predicate(Query &query, Predicate &pred, Arguments &argum } } -void apply_predicate(Query &query, Predicate &predicate, Arguments &arguments, Schema &schema, std::string objectType) +void apply_predicate(Query &query, Predicate &predicate, Arguments &arguments, const Schema &schema, std::string objectType) { update_query_with_predicate(query, predicate, arguments, schema, objectType); diff --git a/parser/query_builder.hpp b/parser/query_builder.hpp index 2d7f9d7c..713ef6ac 100644 --- a/parser/query_builder.hpp +++ b/parser/query_builder.hpp @@ -30,7 +30,7 @@ namespace realm { namespace query_builder { class Arguments; - void apply_predicate(Query &query, parser::Predicate &predicate, Arguments &arguments, Schema &schema, std::string objectType); + void apply_predicate(Query &query, parser::Predicate &predicate, Arguments &arguments, const Schema &schema, std::string objectType); class Arguments { diff --git a/shared_realm.cpp b/shared_realm.cpp index d2919f0c..1dd3f068 100644 --- a/shared_realm.cpp +++ b/shared_realm.cpp @@ -165,7 +165,7 @@ SharedRealm Realm::get_shared_realm(Config config) throw UnitializedRealmException("Can't open an un-initialized Realm without a Schema"); } target_schema->validate(); - ObjectStore::verify_schema(*realm->m_config.schema, *target_schema, true); + ObjectStore::verify_schema(*realm->m_config.schema, const_cast(*target_schema), true); realm->m_config.schema = std::move(target_schema); } else { @@ -180,13 +180,13 @@ SharedRealm Realm::get_shared_realm(Config config) return realm; } -bool Realm::update_schema(std::unique_ptr schema, uint64_t version) +bool Realm::update_schema(std::unique_ptr schema, uint64_t version) { schema->validate(); bool needs_update = !m_config.read_only && (m_config.schema_version != version || ObjectStore::needs_update(*m_config.schema, *schema)); if (!needs_update) { - ObjectStore::verify_schema(*m_config.schema, *schema, m_config.read_only); + ObjectStore::verify_schema(*m_config.schema, const_cast(*schema), m_config.read_only); m_config.schema = std::move(schema); m_config.schema_version = version; return false; @@ -199,9 +199,6 @@ bool Realm::update_schema(std::unique_ptr schema, uint64_t version) old_config.read_only = true; old_config.schema = std::move(old_schema); - m_config.schema = std::move(schema); - m_config.schema_version = version; - auto migration_function = [&](Group*, Schema&) { SharedRealm old_realm(new Realm(old_config)); auto updated_realm = shared_from_this(); @@ -214,8 +211,9 @@ bool Realm::update_schema(std::unique_ptr schema, uint64_t version) // update and migrate begin_transaction(); bool changed = ObjectStore::update_realm_with_schema(read_group(), *old_config.schema, - version, *m_config.schema, - migration_function); + version, const_cast(*schema), migration_function); + m_config.schema = std::move(schema); + m_config.schema_version = version; commit_transaction(); return changed; } diff --git a/shared_realm.hpp b/shared_realm.hpp index 0503282a..49d35cf0 100644 --- a/shared_realm.hpp +++ b/shared_realm.hpp @@ -50,7 +50,7 @@ namespace realm { bool cache = true; std::vector encryption_key; - std::unique_ptr schema; + std::unique_ptr schema; uint64_t schema_version = ObjectStore::NotVersioned; MigrationFunction migration_function; @@ -78,7 +78,7 @@ namespace realm { // on the Config, and the resulting Schema and version with updated // column mappings are set on the realms config upon success. // returns if any changes were made - bool update_schema(std::unique_ptr schema, uint64_t version); + bool update_schema(std::unique_ptr schema, uint64_t version); static uint64_t get_schema_version(Config const& config); From 143564d0b998b9edd1a90ecd30b626844af6f62f Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Thu, 17 Dec 2015 17:32:51 -0800 Subject: [PATCH 53/66] results should store const ObjectSchema --- list.hpp | 2 +- object_accessor.hpp | 2 +- results.cpp | 9 +++++++++ results.hpp | 4 ++-- 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/list.hpp b/list.hpp index 934c56ce..fdde4c62 100644 --- a/list.hpp +++ b/list.hpp @@ -27,7 +27,7 @@ namespace realm { public: List(SharedRealm &r, const ObjectSchema &s, LinkViewRef l) : m_realm(r), object_schema(s), m_link_view(l) {} - const ObjectSchema object_schema; + const ObjectSchema &object_schema; SharedRealm realm() { return m_realm; } size_t size(); diff --git a/object_accessor.hpp b/object_accessor.hpp index 5d290d43..ae1025f0 100644 --- a/object_accessor.hpp +++ b/object_accessor.hpp @@ -27,7 +27,7 @@ namespace realm { template static inline Object create(ContextType ctx, SharedRealm realm, const ObjectSchema &object_schema, ValueType value, bool try_update); - const ObjectSchema object_schema; + const ObjectSchema &object_schema; SharedRealm realm() { return m_realm; } Row row() { return m_row; } diff --git a/results.cpp b/results.cpp index 2f466476..e879c6b7 100644 --- a/results.cpp +++ b/results.cpp @@ -38,6 +38,15 @@ Results::Results(SharedRealm r, const ObjectSchema &o, Table& table) { } +Results& Results::operator=(Results const& r) +{ + m_realm = r.m_realm; + const_cast(object_schema) = r.object_schema; + m_query = r.get_query(); + m_sort = r.get_sort(); + return *this; +} + void Results::validate_read() const { if (m_realm) diff --git a/results.hpp b/results.hpp index 53ee6815..115ca636 100644 --- a/results.hpp +++ b/results.hpp @@ -38,14 +38,14 @@ public: // Results is copyable and moveable Results(Results const&) = default; Results(Results&&) = default; - Results& operator=(Results const&) = default; Results& operator=(Results&&) = default; + Results& operator=(Results const&); // Get the Realm SharedRealm get_realm() const { return m_realm; } // Object schema describing the vendored object type - ObjectSchema object_schema; + const ObjectSchema &object_schema; // Get a query which will match the same rows as is contained in this Results // Returned query will not be valid if the current mode is Empty From 480f4effb24788e6a53194f41f9c04ab2a005d3f Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Thu, 17 Dec 2015 18:40:26 -0800 Subject: [PATCH 54/66] fix for copy assignment --- results.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/results.cpp b/results.cpp index e879c6b7..623b8c6b 100644 --- a/results.cpp +++ b/results.cpp @@ -41,9 +41,11 @@ Results::Results(SharedRealm r, const ObjectSchema &o, Table& table) Results& Results::operator=(Results const& r) { m_realm = r.m_realm; - const_cast(object_schema) = r.object_schema; m_query = r.get_query(); + m_table = r.m_table; m_sort = r.get_sort(); + m_mode = Mode::Query; + const_cast(object_schema) = r.object_schema; return *this; } From e78e33cd98463dacbe7bbace5eeb6a01cd5a9120 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Fri, 18 Dec 2015 14:37:41 -0800 Subject: [PATCH 55/66] pr feedback --- results.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/results.cpp b/results.cpp index 623b8c6b..b2a63789 100644 --- a/results.cpp +++ b/results.cpp @@ -41,9 +41,9 @@ Results::Results(SharedRealm r, const ObjectSchema &o, Table& table) Results& Results::operator=(Results const& r) { m_realm = r.m_realm; - m_query = r.get_query(); m_table = r.m_table; - m_sort = r.get_sort(); + m_sort = r.m_sort; + m_query = r.get_query(); m_mode = Mode::Query; const_cast(object_schema) = r.object_schema; return *this; From f32de945ad8e15d4ba5127991d11f82ee98c5234 Mon Sep 17 00:00:00 2001 From: Scott Kyle Date: Wed, 9 Dec 2015 14:06:25 -0800 Subject: [PATCH 56/66] Update to Realm Core 0.95.5 --- impl/transact_log_handler.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/impl/transact_log_handler.cpp b/impl/transact_log_handler.cpp index 2deba2fd..ac74b515 100644 --- a/impl/transact_log_handler.cpp +++ b/impl/transact_log_handler.cpp @@ -293,10 +293,12 @@ public: // Things that just mark the field as modified bool set_int(size_t col, size_t row, int_fast64_t) { return mark_dirty(row, col); } + bool set_int_unique(size_t col, size_t row, int_fast64_t) { return mark_dirty(row, col); } bool set_bool(size_t col, size_t row, bool) { return mark_dirty(row, col); } bool set_float(size_t col, size_t row, float) { return mark_dirty(row, col); } bool set_double(size_t col, size_t row, double) { return mark_dirty(row, col); } bool set_string(size_t col, size_t row, StringData) { return mark_dirty(row, col); } + bool set_string_unique(size_t col, size_t row, StringData) { return mark_dirty(row, col); } bool set_binary(size_t col, size_t row, BinaryData) { return mark_dirty(row, col); } bool set_date_time(size_t col, size_t row, DateTime) { return mark_dirty(row, col); } bool set_table(size_t col, size_t row) { return mark_dirty(row, col); } From 0286dea7a4dbebb8739554a4ceeb510653a1e85e Mon Sep 17 00:00:00 2001 From: Scott Kyle Date: Fri, 20 Nov 2015 14:05:18 -0800 Subject: [PATCH 57/66] Add methods to create snapshot of List and Results The Results class was updated to match the style of List and include a flag (m_live) that determines if it should sync updates. If an object in the static Results is deleted, then it will return null. --- list.cpp | 5 +++++ list.hpp | 2 ++ results.cpp | 17 +++++++++++++++-- results.hpp | 4 ++++ 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/list.cpp b/list.cpp index 1cbafb8d..80d07bc4 100644 --- a/list.cpp +++ b/list.cpp @@ -59,6 +59,11 @@ void List::remove(size_t row_ndx) { m_link_view->remove(row_ndx); } +Query List::get_query() { + verify_attached(); + return m_link_view->get_target_table().where(m_link_view); +} + void List::verify_valid_row(size_t row_ndx, bool insertion) { size_t size = m_link_view->size(); if (row_ndx > size || (!insertion && row_ndx == size)) { diff --git a/list.hpp b/list.hpp index fdde4c62..85ec6221 100644 --- a/list.hpp +++ b/list.hpp @@ -47,6 +47,8 @@ namespace realm { template void set(ContextType ctx, ValueType value, size_t list_ndx); + Query get_query(); + void verify_valid_row(size_t row_ndx, bool insertion = false); void verify_attached(); void verify_in_tranaction(); diff --git a/results.cpp b/results.cpp index b2a63789..45578796 100644 --- a/results.cpp +++ b/results.cpp @@ -64,6 +64,17 @@ void Results::validate_write() const throw InvalidTransactionException("Must be in a write transaction"); } +void Results::set_live(bool live) +{ + if (!live && m_mode == Mode::Table) { + m_query = m_table->where(); + m_mode = Mode::Query; + } + + update_tableview(); + m_live = live; +} + size_t Results::size() { validate_read(); @@ -91,7 +102,7 @@ RowExpr Results::get(size_t row_ndx) case Mode::TableView: update_tableview(); if (row_ndx < m_table_view.size()) - return m_table_view.get(row_ndx); + return (!m_live && !m_table_view.is_row_attached(row_ndx)) ? RowExpr() : m_table_view.get(row_ndx); break; } @@ -147,7 +158,9 @@ void Results::update_tableview() m_mode = Mode::TableView; break; case Mode::TableView: - m_table_view.sync_if_needed(); + if (m_live) { + m_table_view.sync_if_needed(); + } break; } } diff --git a/results.hpp b/results.hpp index 115ca636..7d8563ff 100644 --- a/results.hpp +++ b/results.hpp @@ -60,6 +60,9 @@ public: // Get the object type which will be returned by get() StringData get_object_type() const noexcept { return object_schema.name; } + // Set whether the TableView should sync if needed before accessing results + void set_live(bool live); + // Get the size of this results // Can be either O(1) or O(N) depending on the state of things size_t size(); @@ -153,6 +156,7 @@ private: TableView m_table_view; Table* m_table = nullptr; SortOrder m_sort; + bool m_live = true; Mode m_mode = Mode::Empty; From b8d40950a723b4f84176ae454c20b09b916c692c Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Mon, 4 Jan 2016 15:54:03 -0800 Subject: [PATCH 58/66] remove invalid usage of const Schema --- results.cpp | 22 ++++++++++------------ shared_realm.cpp | 4 ++-- shared_realm.hpp | 4 ++-- 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/results.cpp b/results.cpp index ea591276..32b00dda 100644 --- a/results.cpp +++ b/results.cpp @@ -171,9 +171,10 @@ size_t Results::index_of(Row const& row) throw DetatchedAccessorException{}; } if (m_table && row.get_table() != m_table) { - throw IncorrectTableException{ - ObjectStore::object_type_for_table_name(m_table->get_name()), - ObjectStore::object_type_for_table_name(row.get_table()->get_name())}; + throw IncorrectTableException(object_schema.name, + ObjectStore::object_type_for_table_name(row.get_table()->get_name()), + "Attempting to get the index of a Row of the wrong type" + ); } return index_of(row.get_index()); } @@ -323,11 +324,6 @@ TableView Results::get_tableview() REALM_UNREACHABLE(); } -StringData Results::get_object_type() const noexcept -{ - return ObjectStore::object_type_for_table_name(m_table->get_name()); -} - Results Results::sort(realm::SortOrder&& sort) const { return Results(m_realm, object_schema, get_query(), std::move(sort)); @@ -338,8 +334,10 @@ Results Results::filter(Query&& q) const return Results(m_realm, object_schema, get_query().and_query(std::move(q)), get_sort()); } -Results::UnsupportedColumnTypeException::UnsupportedColumnTypeException(size_t column, const Table* table) { - column_index = column; - column_name = table->get_column_name(column); - column_type = table->get_column_type(column); +Results::UnsupportedColumnTypeException::UnsupportedColumnTypeException(size_t column, const Table* table) +: column_index(column) +, column_name(table->get_column_name(column)) +, column_type(table->get_column_type(column)) +, std::runtime_error((std::string)"Operation not supported on '" + table->get_column_name(column).data() + "' columns") +{ } diff --git a/shared_realm.cpp b/shared_realm.cpp index aeeeaa28..c43f27db 100644 --- a/shared_realm.cpp +++ b/shared_realm.cpp @@ -193,13 +193,13 @@ SharedRealm Realm::get_shared_realm(Config config) return realm; } -bool Realm::update_schema(std::unique_ptr schema, uint64_t version) +bool Realm::update_schema(std::unique_ptr schema, uint64_t version) { schema->validate(); bool needs_update = !m_config.read_only && (m_config.schema_version != version || ObjectStore::needs_update(*m_config.schema, *schema)); if (!needs_update) { - ObjectStore::verify_schema(*m_config.schema, const_cast(*schema), m_config.read_only); + ObjectStore::verify_schema(*m_config.schema, *schema, m_config.read_only); m_config.schema = std::move(schema); m_config.schema_version = version; return false; diff --git a/shared_realm.hpp b/shared_realm.hpp index ffb86937..85e42224 100644 --- a/shared_realm.hpp +++ b/shared_realm.hpp @@ -52,7 +52,7 @@ namespace realm { bool disable_format_upgrade = false; std::vector encryption_key; - std::unique_ptr schema; + std::unique_ptr schema; uint64_t schema_version = ObjectStore::NotVersioned; MigrationFunction migration_function; @@ -80,7 +80,7 @@ namespace realm { // on the Config, and the resulting Schema and version with updated // column mappings are set on the realms config upon success. // returns if any changes were made - bool update_schema(std::unique_ptr schema, uint64_t version); + bool update_schema(std::unique_ptr schema, uint64_t version); static uint64_t get_schema_version(Config const& config); From c25d08eb3428185236fae5af91227c3425c288bb Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Mon, 4 Jan 2016 16:04:01 -0800 Subject: [PATCH 59/66] store ObjectSchema as a pointer rather than a reference --- list.hpp | 5 +++-- object_accessor.hpp | 6 +++--- results.cpp | 21 +++++---------------- results.hpp | 7 ++++--- 4 files changed, 15 insertions(+), 24 deletions(-) diff --git a/list.hpp b/list.hpp index ef84846a..e7c6465c 100644 --- a/list.hpp +++ b/list.hpp @@ -25,9 +25,9 @@ namespace realm { class List { public: - List(SharedRealm &r, const ObjectSchema &s, LinkViewRef l) : m_realm(r), object_schema(s), m_link_view(l) {} + List(SharedRealm &r, const ObjectSchema &s, LinkViewRef l) : m_realm(r), m_object_schema(&s), m_link_view(l) {} - const ObjectSchema &object_schema; + const ObjectSchema &object_schema() const { return *m_object_schema; } SharedRealm realm() { return m_realm; } size_t size(); @@ -53,6 +53,7 @@ namespace realm { private: SharedRealm m_realm; + const ObjectSchema *m_object_schema; LinkViewRef m_link_view; }; } diff --git a/object_accessor.hpp b/object_accessor.hpp index ae1025f0..eed939e1 100644 --- a/object_accessor.hpp +++ b/object_accessor.hpp @@ -308,19 +308,19 @@ namespace realm { template void List::add(ContextType ctx, ValueType value) { - add(NativeAccessor::to_object_index(ctx, m_realm, value, object_schema.name, false)); + add(NativeAccessor::to_object_index(ctx, m_realm, value, object_schema().name, false)); } template void List::insert(ContextType ctx, ValueType value, size_t list_ndx) { - insert(list_ndx, NativeAccessor::to_object_index(ctx, m_realm, value, object_schema.name, false)); + insert(list_ndx, NativeAccessor::to_object_index(ctx, m_realm, value, object_schema().name, false)); } template void List::set(ContextType ctx, ValueType value, size_t list_ndx) { - set(list_ndx, NativeAccessor::to_object_index(ctx, m_realm, value, object_schema.name, false)); + set(list_ndx, NativeAccessor::to_object_index(ctx, m_realm, value, object_schema().name, false)); } } diff --git a/results.cpp b/results.cpp index 32b00dda..e8d4bfd2 100644 --- a/results.cpp +++ b/results.cpp @@ -40,7 +40,7 @@ Results::Results(SharedRealm r, const ObjectSchema &o, Query q, SortOrder s) , m_table(m_query.get_table().get()) , m_sort(std::move(s)) , m_mode(Mode::Query) -, object_schema(o) +, m_object_schema(&o) { } @@ -48,21 +48,10 @@ Results::Results(SharedRealm r, const ObjectSchema &o, Table& table) : m_realm(std::move(r)) , m_table(&table) , m_mode(Mode::Table) -, object_schema(o) +, m_object_schema(&o) { } -Results& Results::operator=(Results const& r) -{ - m_realm = r.m_realm; - m_table = r.m_table; - m_sort = r.m_sort; - m_query = r.get_query(); - m_mode = Mode::Query; - const_cast(object_schema) = r.object_schema; - return *this; -} - void Results::validate_read() const { if (m_realm) @@ -171,7 +160,7 @@ size_t Results::index_of(Row const& row) throw DetatchedAccessorException{}; } if (m_table && row.get_table() != m_table) { - throw IncorrectTableException(object_schema.name, + throw IncorrectTableException(m_object_schema->name, ObjectStore::object_type_for_table_name(row.get_table()->get_name()), "Attempting to get the index of a Row of the wrong type" ); @@ -326,12 +315,12 @@ TableView Results::get_tableview() Results Results::sort(realm::SortOrder&& sort) const { - return Results(m_realm, object_schema, get_query(), std::move(sort)); + return Results(m_realm, object_schema(), get_query(), std::move(sort)); } Results Results::filter(Query&& q) const { - return Results(m_realm, object_schema, get_query().and_query(std::move(q)), get_sort()); + return Results(m_realm, object_schema(), get_query().and_query(std::move(q)), get_sort()); } Results::UnsupportedColumnTypeException::UnsupportedColumnTypeException(size_t column, const Table* table) diff --git a/results.hpp b/results.hpp index 9866cae8..42dbda8f 100644 --- a/results.hpp +++ b/results.hpp @@ -53,13 +53,13 @@ public: Results(Results const&) = default; Results(Results&&) = default; Results& operator=(Results&&) = default; - Results& operator=(Results const&); + Results& operator=(Results const&) = default; // Get the Realm SharedRealm get_realm() const { return m_realm; } // Object schema describing the vendored object type - const ObjectSchema &object_schema; + const ObjectSchema &object_schema() const { return *m_object_schema; } // Get a query which will match the same rows as is contained in this Results // Returned query will not be valid if the current mode is Empty @@ -72,7 +72,7 @@ public: TableView get_tableview(); // Get the object type which will be returned by get() - StringData get_object_type() const noexcept { return object_schema.name; } + StringData get_object_type() const noexcept { return object_schema().name; } // Get the size of this results // Can be either O(1) or O(N) depending on the state of things @@ -163,6 +163,7 @@ public: private: SharedRealm m_realm; + const ObjectSchema *m_object_schema; Query m_query; TableView m_table_view; Table* m_table = nullptr; From 1e36beb263891bccda0d2e99fcdb2bc139545dac Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Mon, 4 Jan 2016 16:21:28 -0800 Subject: [PATCH 60/66] store ObjectSchema as a pointer rather than a reference --- object_accessor.hpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/object_accessor.hpp b/object_accessor.hpp index eed939e1..dc50240d 100644 --- a/object_accessor.hpp +++ b/object_accessor.hpp @@ -14,7 +14,7 @@ namespace realm { class Object { public: - Object(SharedRealm r, const ObjectSchema &s, Row o) : m_realm(r), object_schema(s), m_row(o) {} + Object(SharedRealm r, const ObjectSchema &s, Row o) : m_realm(r), m_object_schema(&s), m_row(o) {} // property getter/setter template @@ -27,12 +27,13 @@ namespace realm { template static inline Object create(ContextType ctx, SharedRealm realm, const ObjectSchema &object_schema, ValueType value, bool try_update); - const ObjectSchema &object_schema; SharedRealm realm() { return m_realm; } + const ObjectSchema &object_schema() { return *m_object_schema; } Row row() { return m_row; } private: SharedRealm m_realm; + const ObjectSchema *m_object_schema; Row m_row; template @@ -119,10 +120,10 @@ namespace realm { template inline void Object::set_property_value(ContextType ctx, std::string prop_name, ValueType value, bool try_update) { - const Property *prop = object_schema.property_for_name(prop_name); + const Property *prop = m_object_schema->property_for_name(prop_name); if (!prop) { - throw InvalidPropertyException(object_schema.name, prop_name, - "Setting invalid property '" + prop_name + "' on object '" + object_schema.name + "'."); + throw InvalidPropertyException(m_object_schema->name, prop_name, + "Setting invalid property '" + prop_name + "' on object '" + m_object_schema->name + "'."); } set_property_value_impl(ctx, *prop, value, try_update); }; @@ -130,10 +131,10 @@ namespace realm { template inline ValueType Object::get_property_value(ContextType ctx, std::string prop_name) { - const Property *prop = object_schema.property_for_name(prop_name); + const Property *prop = m_object_schema->property_for_name(prop_name); if (!prop) { - throw InvalidPropertyException(object_schema.name, prop_name, - "Getting invalid property '" + prop_name + "' on object '" + object_schema.name + "'."); + throw InvalidPropertyException(m_object_schema->name, prop_name, + "Getting invalid property '" + prop_name + "' on object '" + m_object_schema->name + "'."); } return get_property_value_impl(ctx, *prop); }; From 7964aff43126d4dbd2bfa8eaf69628b6e09fdcfa Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Mon, 4 Jan 2016 16:27:37 -0800 Subject: [PATCH 61/66] remove unnecessary const cast --- shared_realm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared_realm.cpp b/shared_realm.cpp index c43f27db..7c48bf55 100644 --- a/shared_realm.cpp +++ b/shared_realm.cpp @@ -171,7 +171,7 @@ SharedRealm Realm::get_shared_realm(Config config) throw UnitializedRealmException("Can't open an un-initialized Realm without a Schema"); } target_schema->validate(); - ObjectStore::verify_schema(*realm->m_config.schema, const_cast(*target_schema), true); + ObjectStore::verify_schema(*realm->m_config.schema, *target_schema, true); realm->m_config.schema = std::move(target_schema); } else { From 7c116c0629092e76d38fd84faecc9e079f90bd5f Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Mon, 4 Jan 2016 16:37:07 -0800 Subject: [PATCH 62/66] pr fixes --- results.cpp | 4 ++-- shared_realm.cpp | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/results.cpp b/results.cpp index e8d4bfd2..437cfff7 100644 --- a/results.cpp +++ b/results.cpp @@ -324,9 +324,9 @@ Results Results::filter(Query&& q) const } Results::UnsupportedColumnTypeException::UnsupportedColumnTypeException(size_t column, const Table* table) -: column_index(column) +: std::runtime_error((std::string)"Operation not supported on '" + table->get_column_name(column).data() + "' columns") +, column_index(column) , column_name(table->get_column_name(column)) , column_type(table->get_column_type(column)) -, std::runtime_error((std::string)"Operation not supported on '" + table->get_column_name(column).data() + "' columns") { } diff --git a/shared_realm.cpp b/shared_realm.cpp index 7c48bf55..fd85e8f9 100644 --- a/shared_realm.cpp +++ b/shared_realm.cpp @@ -212,6 +212,9 @@ bool Realm::update_schema(std::unique_ptr schema, uint64_t version) old_config.read_only = true; old_config.schema = std::move(old_schema); + m_config.schema = std::move(schema); + m_config.schema_version = version; + auto migration_function = [&](Group*, Schema&) { SharedRealm old_realm(new Realm(old_config)); auto updated_realm = shared_from_this(); @@ -224,9 +227,8 @@ bool Realm::update_schema(std::unique_ptr schema, uint64_t version) // update and migrate begin_transaction(); bool changed = ObjectStore::update_realm_with_schema(read_group(), *old_config.schema, - version, const_cast(*schema), migration_function); - m_config.schema = std::move(schema); - m_config.schema_version = version; + version, *m_config.schema, + migration_function); commit_transaction(); return changed; } From 60b3b5d2fb5972ebc9cc6d832505843882355083 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Mon, 4 Jan 2016 16:51:51 -0800 Subject: [PATCH 63/66] remove unnedded constructors/destructor --- parser/parser.cpp | 2 +- parser/parser.hpp | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/parser/parser.cpp b/parser/parser.cpp index 2753fbd8..7d425ee5 100644 --- a/parser/parser.cpp +++ b/parser/parser.cpp @@ -227,7 +227,7 @@ template<> struct action< or_op > template<> struct action< rule > { \ static void apply( const input & in, ParserState & state ) { \ DEBUG_PRINT_TOKEN(in.string()); \ - state.add_expression(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) diff --git a/parser/parser.hpp b/parser/parser.hpp index 377eda5d..bad15381 100644 --- a/parser/parser.hpp +++ b/parser/parser.hpp @@ -30,8 +30,6 @@ namespace realm { { 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) {} }; struct Predicate @@ -63,7 +61,6 @@ namespace realm { { Operator op = Operator::None; Expression expr[2]; - ~Comparison() {} }; struct Compound From ff532b47c4072fe43f29c3f4de3ba3cea9a4cd96 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Mon, 4 Jan 2016 18:13:09 -0800 Subject: [PATCH 64/66] bdash pr fixes --- list.hpp | 2 +- object_accessor.hpp | 8 ++-- parser/parser.hpp | 98 ++++++++++++++++++++-------------------- parser/query_builder.cpp | 16 +++---- parser/query_builder.hpp | 86 +++++++++++++++++------------------ results.cpp | 4 +- results.hpp | 8 ++-- 7 files changed, 111 insertions(+), 111 deletions(-) diff --git a/list.hpp b/list.hpp index e7c6465c..0440f003 100644 --- a/list.hpp +++ b/list.hpp @@ -27,7 +27,7 @@ namespace realm { public: List(SharedRealm &r, const ObjectSchema &s, LinkViewRef l) : m_realm(r), m_object_schema(&s), m_link_view(l) {} - const ObjectSchema &object_schema() const { return *m_object_schema; } + const ObjectSchema &get_object_schema() const { return *m_object_schema; } SharedRealm realm() { return m_realm; } size_t size(); diff --git a/object_accessor.hpp b/object_accessor.hpp index dc50240d..bad38f9e 100644 --- a/object_accessor.hpp +++ b/object_accessor.hpp @@ -28,7 +28,7 @@ namespace realm { static inline Object create(ContextType ctx, SharedRealm realm, const ObjectSchema &object_schema, ValueType value, bool try_update); SharedRealm realm() { return m_realm; } - const ObjectSchema &object_schema() { return *m_object_schema; } + const ObjectSchema &get_object_schema() { return *m_object_schema; } Row row() { return m_row; } private: @@ -309,19 +309,19 @@ namespace realm { template void List::add(ContextType ctx, ValueType value) { - add(NativeAccessor::to_object_index(ctx, m_realm, value, object_schema().name, false)); + add(NativeAccessor::to_object_index(ctx, m_realm, value, get_object_schema().name, false)); } template void List::insert(ContextType ctx, ValueType value, size_t list_ndx) { - insert(list_ndx, NativeAccessor::to_object_index(ctx, m_realm, value, object_schema().name, false)); + insert(list_ndx, NativeAccessor::to_object_index(ctx, m_realm, value, get_object_schema().name, false)); } template void List::set(ContextType ctx, ValueType value, size_t list_ndx) { - set(list_ndx, NativeAccessor::to_object_index(ctx, m_realm, value, object_schema().name, false)); + set(list_ndx, NativeAccessor::to_object_index(ctx, m_realm, value, get_object_schema().name, false)); } } diff --git a/parser/parser.hpp b/parser/parser.hpp index bad15381..681847f0 100644 --- a/parser/parser.hpp +++ b/parser/parser.hpp @@ -23,64 +23,64 @@ #include namespace realm { - class Schema; +class Schema; - namespace parser { - struct Expression - { - enum class Type { None, Number, String, KeyPath, Argument, True, False } type = Type::None; - std::string s; - }; +namespace parser { +struct Expression +{ + enum class Type { None, Number, String, KeyPath, Argument, True, False } type = Type::None; + std::string s; +}; - struct Predicate - { - enum class Type - { - Comparison, - Or, - And, - True, - False - } type = Type::And; +struct Predicate +{ + enum class Type + { + Comparison, + Or, + And, + True, + False + } type = Type::And; - enum class Operator - { - None, - Equal, - NotEqual, - LessThan, - LessThanOrEqual, - GreaterThan, - GreaterThanOrEqual, - BeginsWith, - EndsWith, - Contains - }; + enum class Operator + { + None, + Equal, + NotEqual, + LessThan, + LessThanOrEqual, + GreaterThan, + GreaterThanOrEqual, + BeginsWith, + EndsWith, + Contains + }; - struct Comparison - { - Operator op = Operator::None; - Expression expr[2]; - }; + struct Comparison + { + Operator op = Operator::None; + Expression expr[2]; + }; - struct Compound - { - std::vector sub_predicates; - }; + struct Compound + { + std::vector sub_predicates; + }; - Comparison cmpr; - Compound cpnd; + Comparison cmpr; + Compound cpnd; - bool negate = false; + bool negate = false; - Predicate(Type t, bool n = false) : type(t), negate(n) {} - }; + Predicate(Type t, bool n = false) : type(t), negate(n) {} +}; - Predicate parse(const std::string &query); - - void analyzeGrammar(); - bool testGrammar(); - } +Predicate parse(const std::string &query); + +void analyzeGrammar(); +bool testGrammar(); +} } #endif // REALM_PARSER_HPP diff --git a/parser/query_builder.cpp b/parser/query_builder.cpp index 6f74f3b2..e3834ee1 100644 --- a/parser/query_builder.cpp +++ b/parser/query_builder.cpp @@ -230,7 +230,7 @@ void add_binary_constraint_to_query(realm::Query &query, void add_link_constraint_to_query(realm::Query &query, Predicate::Operator op, - PropertyExpression &prop_expr, + const PropertyExpression &prop_expr, size_t row_index) { precondition(prop_expr.indexes.empty(), "KeyPath queries not supported for object comparisons."); switch (op) { @@ -260,12 +260,12 @@ void add_link_constraint_to_query(realm::Query &query, } } -auto link_argument(PropertyExpression &propExpr, parser::Expression &argExpr, Arguments &args) +auto link_argument(const PropertyExpression &propExpr, const parser::Expression &argExpr, Arguments &args) { return args.object_index_for_argument(std::stoi(argExpr.s)); } -auto link_argument(parser::Expression &argExpr, PropertyExpression &propExpr, Arguments &args) +auto link_argument(const parser::Expression &argExpr, const PropertyExpression &propExpr, Arguments &args) { return args.object_index_for_argument(std::stoi(argExpr.s)); } @@ -376,7 +376,7 @@ 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, - PropertyExpression &expr, A &lhs, B &rhs, Arguments &args) + const PropertyExpression &expr, A &lhs, B &rhs, Arguments &args) { auto type = expr.prop->type; switch (type) { @@ -418,9 +418,9 @@ void do_add_comparison_to_query(Query &query, const Schema &schema, const Object } } -void add_comparison_to_query(Query &query, Predicate &pred, Arguments &args, const Schema &schema, const std::string &type) +void add_comparison_to_query(Query &query, const Predicate &pred, Arguments &args, const Schema &schema, const std::string &type) { - Predicate::Comparison &cmpr = pred.cmpr; + const Predicate::Comparison &cmpr = pred.cmpr; auto t0 = cmpr.expr[0].type, t1 = cmpr.expr[1].type; auto object_schema = schema.find(type); if (t0 == parser::Expression::Type::KeyPath && t1 != parser::Expression::Type::KeyPath) { @@ -436,7 +436,7 @@ void add_comparison_to_query(Query &query, Predicate &pred, Arguments &args, con } } -void update_query_with_predicate(Query &query, Predicate &pred, Arguments &arguments, const Schema &schema, const std::string &type) +void update_query_with_predicate(Query &query, const Predicate &pred, Arguments &arguments, const Schema &schema, const std::string &type) { if (pred.negate) { query.Not(); @@ -484,7 +484,7 @@ void update_query_with_predicate(Query &query, Predicate &pred, Arguments &argum } } -void apply_predicate(Query &query, Predicate &predicate, Arguments &arguments, const Schema &schema, std::string objectType) +void apply_predicate(Query &query, const Predicate &predicate, Arguments &arguments, const Schema &schema, const std::string &objectType) { update_query_with_predicate(query, predicate, arguments, schema, objectType); diff --git a/parser/query_builder.hpp b/parser/query_builder.hpp index 713ef6ac..e5f519dc 100644 --- a/parser/query_builder.hpp +++ b/parser/query_builder.hpp @@ -24,57 +24,57 @@ #include "object_accessor.hpp" namespace realm { - class Query; - class Schema; +class Query; +class Schema; - namespace query_builder { - class Arguments; +namespace query_builder { +class Arguments; - void apply_predicate(Query &query, parser::Predicate &predicate, Arguments &arguments, const Schema &schema, std::string objectType); +void apply_predicate(Query &query, const parser::Predicate &predicate, Arguments &arguments, const Schema &schema, const std::string &objectType); - class Arguments - { - public: - virtual bool bool_for_argument(size_t argument_index) = 0; - virtual long long long_for_argument(size_t argument_index) = 0; - virtual float float_for_argument(size_t argument_index) = 0; - virtual double double_for_argument(size_t argument_index) = 0; - virtual std::string string_for_argument(size_t argument_index) = 0; - virtual std::string binary_for_argument(size_t argument_index) = 0; - virtual DateTime datetime_for_argument(size_t argument_index) = 0; - virtual size_t object_index_for_argument(size_t argument_index) = 0; - virtual bool is_argument_null(size_t argument_index) = 0; - }; +class Arguments +{ + public: + virtual bool bool_for_argument(size_t argument_index) = 0; + virtual long long long_for_argument(size_t argument_index) = 0; + virtual float float_for_argument(size_t argument_index) = 0; + virtual double double_for_argument(size_t argument_index) = 0; + virtual std::string string_for_argument(size_t argument_index) = 0; + virtual std::string binary_for_argument(size_t argument_index) = 0; + virtual DateTime datetime_for_argument(size_t argument_index) = 0; + virtual size_t object_index_for_argument(size_t argument_index) = 0; + virtual bool is_argument_null(size_t argument_index) = 0; +}; - template - class ArgumentConverter : public Arguments - { - public: - ArgumentConverter(ContextType context, std::vector arguments) : m_arguments(arguments), m_ctx(context) {}; +template +class ArgumentConverter : public Arguments +{ + public: + ArgumentConverter(ContextType context, std::vector arguments) : m_arguments(arguments), m_ctx(context) {}; - using Accessor = realm::NativeAccessor; - virtual bool bool_for_argument(size_t argument_index) { return Accessor::to_bool(m_ctx, argument_at(argument_index)); } - virtual long long long_for_argument(size_t argument_index) { return Accessor::to_long(m_ctx, argument_at(argument_index)); } - virtual float float_for_argument(size_t argument_index) { return Accessor::to_float(m_ctx, argument_at(argument_index)); } - virtual double double_for_argument(size_t argument_index) { return Accessor::to_double(m_ctx, argument_at(argument_index)); } - virtual std::string string_for_argument(size_t argument_index) { return Accessor::to_string(m_ctx, argument_at(argument_index)); } - virtual std::string binary_for_argument(size_t argument_index) { return Accessor::to_binary(m_ctx, argument_at(argument_index)); } - virtual DateTime datetime_for_argument(size_t argument_index) { return Accessor::to_datetime(m_ctx, argument_at(argument_index)); } - virtual size_t object_index_for_argument(size_t argument_index) { return Accessor::to_existing_object_index(m_ctx, argument_at(argument_index)); } - virtual bool is_argument_null(size_t argument_index) { return Accessor::is_null(m_ctx, argument_at(argument_index)); } + using Accessor = realm::NativeAccessor; + virtual bool bool_for_argument(size_t argument_index) { return Accessor::to_bool(m_ctx, argument_at(argument_index)); } + virtual long long long_for_argument(size_t argument_index) { return Accessor::to_long(m_ctx, argument_at(argument_index)); } + virtual float float_for_argument(size_t argument_index) { return Accessor::to_float(m_ctx, argument_at(argument_index)); } + virtual double double_for_argument(size_t argument_index) { return Accessor::to_double(m_ctx, argument_at(argument_index)); } + virtual std::string string_for_argument(size_t argument_index) { return Accessor::to_string(m_ctx, argument_at(argument_index)); } + virtual std::string binary_for_argument(size_t argument_index) { return Accessor::to_binary(m_ctx, argument_at(argument_index)); } + virtual DateTime datetime_for_argument(size_t argument_index) { return Accessor::to_datetime(m_ctx, argument_at(argument_index)); } + virtual size_t object_index_for_argument(size_t argument_index) { return Accessor::to_existing_object_index(m_ctx, argument_at(argument_index)); } + virtual bool is_argument_null(size_t argument_index) { return Accessor::is_null(m_ctx, argument_at(argument_index)); } - private: - std::vector m_arguments; - ContextType m_ctx; + private: + std::vector m_arguments; + ContextType m_ctx; - ValueType &argument_at(size_t index) { - if (index >= m_arguments.size()) { - throw std::out_of_range((std::string)"Argument index " + std::to_string(index) + " out of range 0.." + std::to_string(m_arguments.size()-1)); - } - return m_arguments[index]; - } - }; + ValueType &argument_at(size_t index) { + if (index >= m_arguments.size()) { + throw std::out_of_range((std::string)"Argument index " + std::to_string(index) + " out of range 0.." + std::to_string(m_arguments.size()-1)); + } + return m_arguments[index]; } +}; +} } #endif // REALM_QUERY_BUILDER_HPP diff --git a/results.cpp b/results.cpp index 437cfff7..0839f1f5 100644 --- a/results.cpp +++ b/results.cpp @@ -315,12 +315,12 @@ TableView Results::get_tableview() Results Results::sort(realm::SortOrder&& sort) const { - return Results(m_realm, object_schema(), get_query(), std::move(sort)); + return Results(m_realm, get_object_schema(), get_query(), std::move(sort)); } Results Results::filter(Query&& q) const { - return Results(m_realm, object_schema(), get_query().and_query(std::move(q)), get_sort()); + return Results(m_realm, get_object_schema(), get_query().and_query(std::move(q)), get_sort()); } Results::UnsupportedColumnTypeException::UnsupportedColumnTypeException(size_t column, const Table* table) diff --git a/results.hpp b/results.hpp index 42dbda8f..ec6b7bee 100644 --- a/results.hpp +++ b/results.hpp @@ -46,8 +46,8 @@ public: // or a wrapper around a query and a sort order which creates and updates // the tableview as needed Results() = default; - Results(SharedRealm r, const ObjectSchema &o, Table& table); - Results(SharedRealm r, const ObjectSchema &o, Query q, SortOrder s = {}); + Results(SharedRealm r, const ObjectSchema& o, Table& table); + Results(SharedRealm r, const ObjectSchema& o, Query q, SortOrder s = {}); // Results is copyable and moveable Results(Results const&) = default; @@ -59,7 +59,7 @@ public: SharedRealm get_realm() const { return m_realm; } // Object schema describing the vendored object type - const ObjectSchema &object_schema() const { return *m_object_schema; } + const ObjectSchema &get_object_schema() const { return *m_object_schema; } // Get a query which will match the same rows as is contained in this Results // Returned query will not be valid if the current mode is Empty @@ -72,7 +72,7 @@ public: TableView get_tableview(); // Get the object type which will be returned by get() - StringData get_object_type() const noexcept { return object_schema().name; } + StringData get_object_type() const noexcept { return get_object_schema().name; } // Get the size of this results // Can be either O(1) or O(N) depending on the state of things From d0715cc8e4edc4e9726f7b2eef0dedb20f0fcb8d Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Fri, 8 Jan 2016 11:37:05 -0800 Subject: [PATCH 65/66] pr fixes --- parser/parser.cpp | 2 +- parser/parser.hpp | 4 ++-- parser/query_builder.cpp | 2 +- parser/test.cpp | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/parser/parser.cpp b/parser/parser.cpp index 7d425ee5..2fbcbfbd 100644 --- a/parser/parser.cpp +++ b/parser/parser.cpp @@ -334,7 +334,7 @@ Predicate parse(const std::string &query) return std::move(out_predicate); } -void analyzeGrammar() +void analyze_grammar() { analyze(); } diff --git a/parser/parser.hpp b/parser/parser.hpp index 681847f0..39834dde 100644 --- a/parser/parser.hpp +++ b/parser/parser.hpp @@ -78,8 +78,8 @@ struct Predicate Predicate parse(const std::string &query); -void analyzeGrammar(); -bool testGrammar(); +void analyze_grammar(); +bool test_grammar(); } } diff --git a/parser/query_builder.cpp b/parser/query_builder.cpp index e3834ee1..af0c3d62 100644 --- a/parser/query_builder.cpp +++ b/parser/query_builder.cpp @@ -371,7 +371,7 @@ auto value_of_type_for_query(TableGetter&& tables, Value&& value, Arguments &arg { const bool isColumn = std::is_same::type>::value; using helper = std::conditional_t, ValueGetter>; - return helper::convert(std::forward(tables), std::forward(value), args); + return helper::convert(tables, value, args); } template diff --git a/parser/test.cpp b/parser/test.cpp index ce290474..f834c505 100644 --- a/parser/test.cpp +++ b/parser/test.cpp @@ -134,7 +134,7 @@ static std::vector invalid_queries = { namespace realm { namespace parser { -bool testGrammar() +bool test_grammar() { bool success = true; for (auto &query : valid_queries) { From d4f5f45e56aee4969012f7e9f7d0a9c3673194a4 Mon Sep 17 00:00:00 2001 From: Ari Lazier Date: Fri, 8 Jan 2016 13:05:13 -0800 Subject: [PATCH 66/66] fix for member initializtion ordering --- results.cpp | 4 ++-- results.hpp | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/results.cpp b/results.cpp index c5334652..91a4cc22 100644 --- a/results.cpp +++ b/results.cpp @@ -36,19 +36,19 @@ using namespace realm; Results::Results(SharedRealm r, const ObjectSchema &o, Query q, SortOrder s) : m_realm(std::move(r)) +, m_object_schema(&o) , m_query(std::move(q)) , m_table(m_query.get_table().get()) , m_sort(std::move(s)) , m_mode(Mode::Query) -, m_object_schema(&o) { } Results::Results(SharedRealm r, const ObjectSchema &o, Table& table) : m_realm(std::move(r)) +, m_object_schema(&o) , m_table(&table) , m_mode(Mode::Table) -, m_object_schema(&o) { } diff --git a/results.hpp b/results.hpp index 6ce19a77..27127e8d 100644 --- a/results.hpp +++ b/results.hpp @@ -135,9 +135,10 @@ public: // The input index parameter was out of bounds struct OutOfBoundsIndexException : public std::out_of_range { - OutOfBoundsIndexException(size_t r, size_t c) : requested(r), valid_count(c), + OutOfBoundsIndexException(size_t r, size_t c) : std::out_of_range((std::string)"Requested index " + std::to_string(r) + - " greater than max " + std::to_string(c)) {} + " greater than max " + std::to_string(c)), + requested(r), valid_count(c) {} const size_t requested; const size_t valid_count; }; @@ -150,7 +151,7 @@ public: // The input Row object belongs to a different table struct IncorrectTableException : public std::runtime_error { IncorrectTableException(StringData e, StringData a, const std::string &error) : - expected(e), actual(a), std::runtime_error(error) {} + std::runtime_error(error), expected(e), actual(a) {} const StringData expected; const StringData actual; };