Merge pull request #29 from realm/al-js-fixes
Outstanding ObjectStore changes from the JS branch
This commit is contained in:
commit
dd2c87c3b7
5
list.cpp
5
list.cpp
|
@ -60,6 +60,11 @@ void List::remove(std::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(std::size_t row_ndx, bool insertion) {
|
||||
size_t size = m_link_view->size();
|
||||
if (row_ndx > size || (!insertion && row_ndx == size)) {
|
||||
|
|
9
list.hpp
9
list.hpp
|
@ -25,9 +25,9 @@
|
|||
namespace realm {
|
||||
class List {
|
||||
public:
|
||||
List(SharedRealm &r, const ObjectSchema &s, LinkViewRef l) : object_schema(s), m_realm(r), 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 &get_object_schema() const { return *m_object_schema; }
|
||||
SharedRealm realm() { return m_realm; }
|
||||
|
||||
size_t size();
|
||||
|
@ -47,16 +47,17 @@ namespace realm {
|
|||
template<typename ValueType, typename ContextType>
|
||||
void set(ContextType ctx, ValueType value, size_t list_ndx);
|
||||
|
||||
Query get_query();
|
||||
|
||||
void verify_valid_row(std::size_t row_ndx, bool insertion = false);
|
||||
void verify_attached();
|
||||
void verify_in_tranaction();
|
||||
|
||||
private:
|
||||
SharedRealm m_realm;
|
||||
const ObjectSchema *m_object_schema;
|
||||
LinkViewRef m_link_view;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif /* REALM_LIST_HPP */
|
||||
|
|
|
@ -7,13 +7,14 @@
|
|||
|
||||
#include <string>
|
||||
#include "shared_realm.hpp"
|
||||
#include "schema.hpp"
|
||||
#include "list.hpp"
|
||||
|
||||
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<typename ValueType, typename ContextType>
|
||||
|
@ -24,14 +25,15 @@ namespace realm {
|
|||
|
||||
// create an Object from a native representation
|
||||
template<typename ValueType, typename ContextType>
|
||||
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; }
|
||||
const ObjectSchema &get_object_schema() { return *m_object_schema; }
|
||||
Row row() { return m_row; }
|
||||
|
||||
private:
|
||||
SharedRealm m_realm;
|
||||
const ObjectSchema *m_object_schema;
|
||||
Row m_row;
|
||||
|
||||
template<typename ValueType, typename ContextType>
|
||||
|
@ -76,6 +78,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);
|
||||
|
@ -115,10 +120,10 @@ namespace realm {
|
|||
template <typename ValueType, typename ContextType>
|
||||
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);
|
||||
};
|
||||
|
@ -126,10 +131,10 @@ namespace realm {
|
|||
template <typename ValueType, typename ContextType>
|
||||
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<ValueType>(ctx, *prop);
|
||||
};
|
||||
|
@ -239,7 +244,7 @@ namespace realm {
|
|||
}
|
||||
|
||||
template<typename ValueType, typename ContextType>
|
||||
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<ValueType, ContextType>;
|
||||
|
||||
|
@ -279,7 +284,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);
|
||||
|
@ -304,19 +309,19 @@ namespace realm {
|
|||
template<typename ValueType, typename ContextType>
|
||||
void List::add(ContextType ctx, ValueType value)
|
||||
{
|
||||
add(NativeAccessor<ValueType, ContextType>::to_object_index(ctx, m_realm, value, object_schema.name, false));
|
||||
add(NativeAccessor<ValueType, ContextType>::to_object_index(ctx, m_realm, value, get_object_schema().name, false));
|
||||
}
|
||||
|
||||
template<typename ValueType, typename ContextType>
|
||||
void List::insert(ContextType ctx, ValueType value, size_t list_ndx)
|
||||
{
|
||||
insert(list_ndx, NativeAccessor<ValueType, ContextType>::to_object_index(ctx, m_realm, value, object_schema.name, false));
|
||||
insert(list_ndx, NativeAccessor<ValueType, ContextType>::to_object_index(ctx, m_realm, value, get_object_schema().name, false));
|
||||
}
|
||||
|
||||
template<typename ValueType, typename ContextType>
|
||||
void List::set(ContextType ctx, ValueType value, size_t list_ndx)
|
||||
{
|
||||
set(list_ndx, NativeAccessor<ValueType, ContextType>::to_object_index(ctx, m_realm, value, object_schema.name, false));
|
||||
set(list_ndx, NativeAccessor<ValueType, ContextType>::to_object_index(ctx, m_realm, value, get_object_schema().name, false));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,344 @@
|
|||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <iostream>
|
||||
|
||||
#include <pegtl.hh>
|
||||
#include <pegtl/analyze.hh>
|
||||
#include <pegtl/trace.hh>
|
||||
|
||||
using namespace pegtl;
|
||||
|
||||
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', '0' > {};
|
||||
struct escaped : sor< escaped_char, unicode > {};
|
||||
struct unescaped : utf8::range< 0x20, 0x10FFFF > {};
|
||||
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< chars > > {};
|
||||
struct sq_string : seq< one< '\'' >, must< sq_string_content >, any > {};
|
||||
|
||||
// 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 > > {};
|
||||
|
||||
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< '.' > > {};
|
||||
|
||||
// argument
|
||||
struct argument_index : plus< digit > {};
|
||||
struct argument : seq< one< '$' >, must< argument_index > > {};
|
||||
|
||||
// expressions and operators
|
||||
struct expr : sor< dq_string, sq_string, number, argument, true_value, false_value, key_path > {};
|
||||
|
||||
struct eq : sor< two< '=' >, one< '=' > > {};
|
||||
struct noteq : pegtl::string< '!', '=' > {};
|
||||
struct lteq : pegtl::string< '<', '=' > {};
|
||||
struct lt : one< '<' > {};
|
||||
struct gteq : pegtl::string< '>', '=' > {};
|
||||
struct gt : one< '>' > {};
|
||||
struct contains : pegtl_istring_t("contains") {};
|
||||
struct begins : pegtl_istring_t("beginswith") {};
|
||||
struct ends : pegtl_istring_t("endswith") {};
|
||||
|
||||
template<typename A, typename B>
|
||||
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, sor< padded_oper, symbolic_oper >, expr > {};
|
||||
|
||||
struct pred;
|
||||
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< '!' >, 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 > {};
|
||||
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 > {};
|
||||
struct and_pred : seq< atom_pred, star< and_ext > > {};
|
||||
|
||||
struct pred : seq< and_pred, star< or_ext > > {};
|
||||
|
||||
// state
|
||||
struct ParserState
|
||||
{
|
||||
std::vector<Predicate *> group_stack;
|
||||
|
||||
Predicate *current_group()
|
||||
{
|
||||
return group_stack.back();
|
||||
}
|
||||
|
||||
Predicate *last_predicate()
|
||||
{
|
||||
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 add_expression(Expression && exp)
|
||||
{
|
||||
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 {
|
||||
add_predicate_to_current_group(Predicate::Type::Comparison);
|
||||
last_predicate()->cmpr.expr[0] = std::move(exp);
|
||||
}
|
||||
}
|
||||
|
||||
void apply_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) {
|
||||
// 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);
|
||||
|
||||
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 && !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();
|
||||
}
|
||||
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));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// rules
|
||||
template< typename Rule >
|
||||
struct action : nothing< Rule > {};
|
||||
|
||||
#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_op >
|
||||
{
|
||||
static void apply( const input & in, ParserState & state )
|
||||
{
|
||||
DEBUG_PRINT_TOKEN("<and>");
|
||||
state.next_type = Predicate::Type::And;
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct action< or_op >
|
||||
{
|
||||
static void apply( const input & in, ParserState & state )
|
||||
{
|
||||
DEBUG_PRINT_TOKEN("<or>");
|
||||
state.next_type = Predicate::Type::Or;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#define EXPRESSION_ACTION(rule, type) \
|
||||
template<> struct action< rule > { \
|
||||
static void apply( const input & in, ParserState & state ) { \
|
||||
DEBUG_PRINT_TOKEN(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)
|
||||
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)
|
||||
|
||||
|
||||
template<> struct action< true_pred >
|
||||
{
|
||||
static void apply( const input & in, ParserState & state )
|
||||
{
|
||||
DEBUG_PRINT_TOKEN(in.string());
|
||||
state.current_group()->cpnd.sub_predicates.emplace_back(Predicate::Type::True);
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct action< false_pred >
|
||||
{
|
||||
static void apply( const input & in, ParserState & state )
|
||||
{
|
||||
DEBUG_PRINT_TOKEN(in.string());
|
||||
state.current_group()->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 ) { \
|
||||
DEBUG_PRINT_TOKEN(in.string()); \
|
||||
state.last_predicate()->cmpr.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 )
|
||||
{
|
||||
DEBUG_PRINT_TOKEN("<begin_group>");
|
||||
state.add_predicate_to_current_group(Predicate::Type::And);
|
||||
state.group_stack.push_back(state.last_predicate());
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct action< group_pred >
|
||||
{
|
||||
static void apply( const input & in, ParserState & state )
|
||||
{
|
||||
DEBUG_PRINT_TOKEN("<end_group>");
|
||||
state.group_stack.pop_back();
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct action< not_pre >
|
||||
{
|
||||
static void apply( const input & in, ParserState & state )
|
||||
{
|
||||
DEBUG_PRINT_TOKEN("<not>");
|
||||
state.negate_next = true;
|
||||
}
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
DEBUG_PRINT_TOKEN(query);
|
||||
|
||||
Predicate out_predicate(Predicate::Type::And);
|
||||
|
||||
ParserState state;
|
||||
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) {
|
||||
return std::move(out_predicate.cpnd.sub_predicates.back());
|
||||
}
|
||||
return std::move(out_predicate);
|
||||
}
|
||||
|
||||
void analyze_grammar()
|
||||
{
|
||||
analyze<pred>();
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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_PARSER_HPP
|
||||
#define REALM_PARSER_HPP
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace realm {
|
||||
class Schema;
|
||||
|
||||
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;
|
||||
|
||||
enum class Operator
|
||||
{
|
||||
None,
|
||||
Equal,
|
||||
NotEqual,
|
||||
LessThan,
|
||||
LessThanOrEqual,
|
||||
GreaterThan,
|
||||
GreaterThanOrEqual,
|
||||
BeginsWith,
|
||||
EndsWith,
|
||||
Contains
|
||||
};
|
||||
|
||||
struct Comparison
|
||||
{
|
||||
Operator op = Operator::None;
|
||||
Expression expr[2];
|
||||
};
|
||||
|
||||
struct Compound
|
||||
{
|
||||
std::vector<Predicate> sub_predicates;
|
||||
};
|
||||
|
||||
Comparison cmpr;
|
||||
Compound cpnd;
|
||||
|
||||
bool negate = false;
|
||||
|
||||
Predicate(Type t, bool n = false) : type(t), negate(n) {}
|
||||
};
|
||||
|
||||
Predicate parse(const std::string &query);
|
||||
|
||||
void analyze_grammar();
|
||||
bool test_grammar();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // REALM_PARSER_HPP
|
|
@ -0,0 +1,322 @@
|
|||
{
|
||||
|
||||
"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": "object", "objectType": "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"]]
|
||||
]
|
||||
},
|
||||
|
||||
"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", [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"],
|
||||
["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"]
|
||||
]
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,496 @@
|
|||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <realm.hpp>
|
||||
#include "object_store.hpp"
|
||||
#include "schema.hpp"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
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
|
||||
#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 {
|
||||
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; }
|
||||
};
|
||||
|
||||
using KeyPath = std::vector<std::string>;
|
||||
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
|
||||
{
|
||||
const Property *prop = nullptr;
|
||||
std::vector<size_t> indexes;
|
||||
std::function<Table *()> table_getter;
|
||||
|
||||
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++) {
|
||||
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 <typename A, typename B>
|
||||
void add_numeric_constraint_to_query(Query& query,
|
||||
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 <typename A, typename B>
|
||||
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<String> &&column,
|
||||
std::string &&value) {
|
||||
bool case_sensitive = true;
|
||||
switch (op) {
|
||||
case Predicate::Operator::BeginsWith:
|
||||
query.and_query(column.begins_with(value, case_sensitive));
|
||||
break;
|
||||
case Predicate::Operator::EndsWith:
|
||||
query.and_query(column.ends_with(value, case_sensitive));
|
||||
break;
|
||||
case Predicate::Operator::Contains:
|
||||
query.and_query(column.contains(value, case_sensitive));
|
||||
break;
|
||||
case Predicate::Operator::Equal:
|
||||
query.and_query(column.equal(value, case_sensitive));
|
||||
break;
|
||||
case Predicate::Operator::NotEqual:
|
||||
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,
|
||||
Predicate::Operator op,
|
||||
std::string &&value,
|
||||
Columns<String> &&column) {
|
||||
bool case_sensitive = true;
|
||||
switch (op) {
|
||||
case Predicate::Operator::Equal:
|
||||
query.and_query(column.equal(value, case_sensitive));
|
||||
break;
|
||||
case Predicate::Operator::NotEqual:
|
||||
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<Binary> &&column,
|
||||
std::string &&value) {
|
||||
switch (op) {
|
||||
case Predicate::Operator::BeginsWith:
|
||||
query.begins_with(column.m_column, BinaryData(value));
|
||||
break;
|
||||
case Predicate::Operator::EndsWith:
|
||||
query.ends_with(column.m_column, BinaryData(value));
|
||||
break;
|
||||
case Predicate::Operator::Contains:
|
||||
query.contains(column.m_column, BinaryData(value));
|
||||
break;
|
||||
case Predicate::Operator::Equal:
|
||||
query.equal(column.m_column, BinaryData(value));
|
||||
break;
|
||||
case Predicate::Operator::NotEqual:
|
||||
query.not_equal(column.m_column, BinaryData(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<Binary> &&column) {
|
||||
switch (op) {
|
||||
case Predicate::Operator::Equal:
|
||||
query.equal(column.m_column, BinaryData(value));
|
||||
break;
|
||||
case Predicate::Operator::NotEqual:
|
||||
query.not_equal(column.m_column, BinaryData(value));
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("Substring comparison not supported for keypath substrings.");
|
||||
}
|
||||
}
|
||||
|
||||
void add_link_constraint_to_query(realm::Query &query,
|
||||
Predicate::Operator op,
|
||||
const 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.");
|
||||
}
|
||||
}
|
||||
|
||||
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<Link>(prop_expr.prop->table_column).is_null());
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("Only 'equal' and 'not equal' operators supported for object comparison.");
|
||||
}
|
||||
}
|
||||
|
||||
auto link_argument(const PropertyExpression &propExpr, const parser::Expression &argExpr, Arguments &args)
|
||||
{
|
||||
return args.object_index_for_argument(std::stoi(argExpr.s));
|
||||
}
|
||||
|
||||
auto link_argument(const parser::Expression &argExpr, const PropertyExpression &propExpr, Arguments &args)
|
||||
{
|
||||
return args.object_index_for_argument(std::stoi(argExpr.s));
|
||||
}
|
||||
|
||||
|
||||
template <typename RetType, typename TableGetter>
|
||||
struct ColumnGetter {
|
||||
static Columns<RetType> convert(TableGetter&& table, const PropertyExpression & expr, Arguments &args)
|
||||
{
|
||||
return table()->template column<RetType>(expr.prop->table_column);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename RequestedType, typename TableGetter>
|
||||
struct ValueGetter;
|
||||
|
||||
template <typename TableGetter>
|
||||
struct ValueGetter<DateTime, TableGetter> {
|
||||
static Int convert(TableGetter&&, const parser::Expression & value, Arguments &args)
|
||||
{
|
||||
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 <typename TableGetter>
|
||||
struct ValueGetter<bool, TableGetter> {
|
||||
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));
|
||||
}
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename TableGetter>
|
||||
struct ValueGetter<Double, TableGetter> {
|
||||
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 <typename TableGetter>
|
||||
struct ValueGetter<Float, TableGetter> {
|
||||
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 <typename TableGetter>
|
||||
struct ValueGetter<Int, TableGetter> {
|
||||
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 <typename TableGetter>
|
||||
struct ValueGetter<String, TableGetter> {
|
||||
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));
|
||||
}
|
||||
if (value.type != parser::Expression::Type::String) {
|
||||
throw std::runtime_error("Attempting to compare String property to a non-String value");
|
||||
}
|
||||
return value.s;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename TableGetter>
|
||||
struct ValueGetter<Binary, TableGetter> {
|
||||
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 <typename RetType, typename Value, typename TableGetter>
|
||||
auto value_of_type_for_query(TableGetter&& tables, Value&& value, Arguments &args)
|
||||
{
|
||||
const bool isColumn = std::is_same<PropertyExpression, typename std::remove_reference<Value>::type>::value;
|
||||
using helper = std::conditional_t<isColumn, ColumnGetter<RetType, TableGetter>, ValueGetter<RetType, TableGetter>>;
|
||||
return helper::convert(tables, value, args);
|
||||
}
|
||||
|
||||
template <typename A, typename B>
|
||||
void do_add_comparison_to_query(Query &query, const Schema &schema, const ObjectSchema &object_schema, Predicate::Operator op,
|
||||
const PropertyExpression &expr, A &lhs, B &rhs, Arguments &args)
|
||||
{
|
||||
auto type = expr.prop->type;
|
||||
switch (type) {
|
||||
case PropertyTypeBool:
|
||||
add_bool_constraint_to_query(query, op, value_of_type_for_query<bool>(expr.table_getter, lhs, args),
|
||||
value_of_type_for_query<bool>(expr.table_getter, rhs, args));
|
||||
break;
|
||||
case PropertyTypeDate:
|
||||
add_numeric_constraint_to_query(query, op, value_of_type_for_query<DateTime>(expr.table_getter, lhs, args),
|
||||
value_of_type_for_query<DateTime>(expr.table_getter, rhs, args));
|
||||
break;
|
||||
case PropertyTypeDouble:
|
||||
add_numeric_constraint_to_query(query, op, value_of_type_for_query<Double>(expr.table_getter, lhs, args),
|
||||
value_of_type_for_query<Double>(expr.table_getter, rhs, args));
|
||||
break;
|
||||
case PropertyTypeFloat:
|
||||
add_numeric_constraint_to_query(query, op, value_of_type_for_query<Float>(expr.table_getter, lhs, args),
|
||||
value_of_type_for_query<Float>(expr.table_getter, rhs, args));
|
||||
break;
|
||||
case PropertyTypeInt:
|
||||
add_numeric_constraint_to_query(query, op, value_of_type_for_query<Int>(expr.table_getter, lhs, args),
|
||||
value_of_type_for_query<Int>(expr.table_getter, rhs, args));
|
||||
break;
|
||||
case PropertyTypeString:
|
||||
add_string_constraint_to_query(query, op, value_of_type_for_query<String>(expr.table_getter, lhs, args),
|
||||
value_of_type_for_query<String>(expr.table_getter, rhs, args));
|
||||
break;
|
||||
case PropertyTypeData:
|
||||
add_binary_constraint_to_query(query, op, value_of_type_for_query<Binary>(expr.table_getter, lhs, args),
|
||||
value_of_type_for_query<Binary>(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, const Predicate &pred, Arguments &args, const Schema &schema, const std::string &type)
|
||||
{
|
||||
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) {
|
||||
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) {
|
||||
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, const Predicate &pred, Arguments &arguments, const Schema &schema, const std::string &type)
|
||||
{
|
||||
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, arguments, schema, type);
|
||||
}
|
||||
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, arguments, schema, type);
|
||||
}
|
||||
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, arguments, schema, type);
|
||||
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, const Predicate &predicate, Arguments &arguments, const Schema &schema, const std::string &objectType)
|
||||
{
|
||||
update_query_with_predicate(query, predicate, arguments, schema, objectType);
|
||||
|
||||
// Test the constructed query in core
|
||||
std::string validateMessage = query.validate();
|
||||
precondition(validateMessage.empty(), validateMessage.c_str());
|
||||
}
|
||||
|
||||
}}
|
|
@ -0,0 +1,80 @@
|
|||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <string>
|
||||
#include "parser.hpp"
|
||||
#include "object_accessor.hpp"
|
||||
|
||||
namespace realm {
|
||||
class Query;
|
||||
class Schema;
|
||||
|
||||
namespace query_builder {
|
||||
class Arguments;
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
template<typename ValueType, typename ContextType>
|
||||
class ArgumentConverter : public Arguments
|
||||
{
|
||||
public:
|
||||
ArgumentConverter(ContextType context, std::vector<ValueType> arguments) : m_arguments(arguments), m_ctx(context) {};
|
||||
|
||||
using Accessor = realm::NativeAccessor<ValueType, ContextType>;
|
||||
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<ValueType> 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];
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif // REALM_QUERY_BUILDER_HPP
|
|
@ -0,0 +1,166 @@
|
|||
|
||||
#include "parser.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
|
||||
static std::vector<std::string> valid_queries = {
|
||||
// true/false predicates
|
||||
"truepredicate",
|
||||
"falsepredicate",
|
||||
" TRUEPREDICATE ",
|
||||
" FALSEPREDICATE ",
|
||||
|
||||
// characters/strings
|
||||
"\"\" = ''",
|
||||
"'azAZ09/ :()[]{}<>,.^@-+=*&~`' = '\\\" \\' \\\\ \\/ \\b \\f \\n \\r \\t \\0'",
|
||||
"\"azAZ09/\" = \"\\\" \\' \\\\ \\/ \\b \\f \\n \\r \\t \\0\"",
|
||||
"'\\uffFf' = '\\u0020'",
|
||||
"'\\u01111' = 'asdf\\u0111asdf'",
|
||||
|
||||
// expressions (numbers, bools, keypaths, arguments)
|
||||
"-1 = 12",
|
||||
"0 = 001",
|
||||
"0x0 = -0X398235fcAb",
|
||||
"10. = -.034",
|
||||
"10.0 = 5.034",
|
||||
"true = false",
|
||||
"_ = a",
|
||||
"_a = _.aZ",
|
||||
"a09._br.z = __-__.Z-9",
|
||||
"$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",
|
||||
|
||||
// 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)",
|
||||
|
||||
// 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",
|
||||
"a=1AND NOTb=2",
|
||||
};
|
||||
|
||||
static std::vector<std::string> invalid_queries = {
|
||||
"predicate",
|
||||
"'\\a' = ''", // invalid escape
|
||||
|
||||
// invalid unicode
|
||||
"'\\u0' = ''",
|
||||
|
||||
// invalid strings
|
||||
"\"' = ''",
|
||||
"\" = ''",
|
||||
"' = ''",
|
||||
|
||||
// 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",
|
||||
"$ = $",
|
||||
|
||||
// operators
|
||||
"0===>0",
|
||||
"0 <> 0",
|
||||
"0 contains1",
|
||||
"endswith 0",
|
||||
|
||||
// atoms/groups
|
||||
"0=0)",
|
||||
"(0=0",
|
||||
"(0=0))",
|
||||
"! =0",
|
||||
"NOTNOT(0=0)",
|
||||
"(!!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",
|
||||
//"a=1ANDNOT b=2",
|
||||
|
||||
"truepredicate &&",
|
||||
"truepredicate & truepredicate",
|
||||
};
|
||||
|
||||
namespace realm {
|
||||
namespace parser {
|
||||
|
||||
bool test_grammar()
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
success = false;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
# /bin/sh
|
||||
llvm-g++ -std=c++11 -I ../../../vendor/PEGTL/ -o test test.cpp parser.cpp
|
||||
./test
|
49
results.cpp
49
results.cpp
|
@ -34,8 +34,9 @@ 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_object_schema(&o)
|
||||
, m_query(std::move(q))
|
||||
, m_table(m_query.get_table().get())
|
||||
, m_sort(std::move(s))
|
||||
|
@ -43,8 +44,9 @@ Results::Results(SharedRealm r, Query q, SortOrder s)
|
|||
{
|
||||
}
|
||||
|
||||
Results::Results(SharedRealm r, Table& table)
|
||||
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)
|
||||
{
|
||||
|
@ -65,6 +67,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();
|
||||
|
@ -92,7 +105,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;
|
||||
}
|
||||
|
||||
|
@ -146,7 +159,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;
|
||||
}
|
||||
}
|
||||
|
@ -158,9 +173,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(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"
|
||||
);
|
||||
}
|
||||
return index_of(row.get_index());
|
||||
}
|
||||
|
@ -310,23 +326,20 @@ 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, get_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, get_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)
|
||||
: 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))
|
||||
{
|
||||
}
|
||||
|
|
49
results.hpp
49
results.hpp
|
@ -46,15 +46,21 @@ 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;
|
||||
Results(Results&&) = default;
|
||||
Results& operator=(Results const&) = default;
|
||||
Results& operator=(Results&&) = default;
|
||||
Results& operator=(Results const&) = default;
|
||||
|
||||
// Get the Realm
|
||||
SharedRealm get_realm() const { return m_realm; }
|
||||
|
||||
// Object schema describing the vendored object type
|
||||
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
|
||||
Query get_query() const;
|
||||
|
@ -66,7 +72,10 @@ 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 get_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
|
||||
|
@ -118,25 +127,37 @@ 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) :
|
||||
std::out_of_range((std::string)"Requested index " + std::to_string(r) +
|
||||
" greater than max " + std::to_string(c)),
|
||||
requested(r), valid_count(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) :
|
||||
std::runtime_error(error), expected(e), actual(a) {}
|
||||
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;
|
||||
|
@ -146,10 +167,12 @@ public:
|
|||
|
||||
private:
|
||||
SharedRealm m_realm;
|
||||
const ObjectSchema *m_object_schema;
|
||||
Query m_query;
|
||||
TableView m_table_view;
|
||||
Table* m_table = nullptr;
|
||||
SortOrder m_sort;
|
||||
bool m_live = true;
|
||||
|
||||
Mode m_mode = Mode::Empty;
|
||||
|
||||
|
|
|
@ -402,8 +402,6 @@ uint64_t Realm::get_schema_version(const realm::Realm::Config &config)
|
|||
|
||||
void Realm::close()
|
||||
{
|
||||
invalidate();
|
||||
|
||||
if (m_notifier) {
|
||||
m_notifier->remove_realm(this);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue