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);
|
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) {
|
void List::verify_valid_row(std::size_t row_ndx, bool insertion) {
|
||||||
size_t size = m_link_view->size();
|
size_t size = m_link_view->size();
|
||||||
if (row_ndx > size || (!insertion && row_ndx == size)) {
|
if (row_ndx > size || (!insertion && row_ndx == size)) {
|
||||||
|
|
9
list.hpp
9
list.hpp
|
@ -25,9 +25,9 @@
|
||||||
namespace realm {
|
namespace realm {
|
||||||
class List {
|
class List {
|
||||||
public:
|
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; }
|
SharedRealm realm() { return m_realm; }
|
||||||
|
|
||||||
size_t size();
|
size_t size();
|
||||||
|
@ -47,16 +47,17 @@ namespace realm {
|
||||||
template<typename ValueType, typename ContextType>
|
template<typename ValueType, typename ContextType>
|
||||||
void set(ContextType ctx, ValueType value, size_t list_ndx);
|
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_valid_row(std::size_t row_ndx, bool insertion = false);
|
||||||
void verify_attached();
|
void verify_attached();
|
||||||
void verify_in_tranaction();
|
void verify_in_tranaction();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SharedRealm m_realm;
|
SharedRealm m_realm;
|
||||||
|
const ObjectSchema *m_object_schema;
|
||||||
LinkViewRef m_link_view;
|
LinkViewRef m_link_view;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif /* REALM_LIST_HPP */
|
#endif /* REALM_LIST_HPP */
|
||||||
|
|
|
@ -7,13 +7,14 @@
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "shared_realm.hpp"
|
#include "shared_realm.hpp"
|
||||||
|
#include "schema.hpp"
|
||||||
#include "list.hpp"
|
#include "list.hpp"
|
||||||
|
|
||||||
namespace realm {
|
namespace realm {
|
||||||
|
|
||||||
class Object {
|
class Object {
|
||||||
public:
|
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
|
// property getter/setter
|
||||||
template<typename ValueType, typename ContextType>
|
template<typename ValueType, typename ContextType>
|
||||||
|
@ -24,14 +25,15 @@ namespace realm {
|
||||||
|
|
||||||
// create an Object from a native representation
|
// create an Object from a native representation
|
||||||
template<typename ValueType, typename ContextType>
|
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; }
|
SharedRealm realm() { return m_realm; }
|
||||||
|
const ObjectSchema &get_object_schema() { return *m_object_schema; }
|
||||||
Row row() { return m_row; }
|
Row row() { return m_row; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SharedRealm m_realm;
|
SharedRealm m_realm;
|
||||||
|
const ObjectSchema *m_object_schema;
|
||||||
Row m_row;
|
Row m_row;
|
||||||
|
|
||||||
template<typename ValueType, typename ContextType>
|
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 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);
|
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
|
// list value acessors
|
||||||
static size_t list_size(ContextType ctx, ValueType &val);
|
static size_t list_size(ContextType ctx, ValueType &val);
|
||||||
static ValueType list_value_at_index(ContextType ctx, ValueType &val, size_t index);
|
static ValueType list_value_at_index(ContextType ctx, ValueType &val, size_t index);
|
||||||
|
@ -115,10 +120,10 @@ namespace realm {
|
||||||
template <typename ValueType, typename ContextType>
|
template <typename ValueType, typename ContextType>
|
||||||
inline void Object::set_property_value(ContextType ctx, std::string prop_name, ValueType value, bool try_update)
|
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) {
|
if (!prop) {
|
||||||
throw InvalidPropertyException(object_schema.name, prop_name,
|
throw InvalidPropertyException(m_object_schema->name, prop_name,
|
||||||
"Setting invalid property '" + prop_name + "' on object '" + object_schema.name + "'.");
|
"Setting invalid property '" + prop_name + "' on object '" + m_object_schema->name + "'.");
|
||||||
}
|
}
|
||||||
set_property_value_impl(ctx, *prop, value, try_update);
|
set_property_value_impl(ctx, *prop, value, try_update);
|
||||||
};
|
};
|
||||||
|
@ -126,10 +131,10 @@ namespace realm {
|
||||||
template <typename ValueType, typename ContextType>
|
template <typename ValueType, typename ContextType>
|
||||||
inline ValueType Object::get_property_value(ContextType ctx, std::string prop_name)
|
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) {
|
if (!prop) {
|
||||||
throw InvalidPropertyException(object_schema.name, prop_name,
|
throw InvalidPropertyException(m_object_schema->name, prop_name,
|
||||||
"Getting invalid property '" + prop_name + "' on object '" + object_schema.name + "'.");
|
"Getting invalid property '" + prop_name + "' on object '" + m_object_schema->name + "'.");
|
||||||
}
|
}
|
||||||
return get_property_value_impl<ValueType>(ctx, *prop);
|
return get_property_value_impl<ValueType>(ctx, *prop);
|
||||||
};
|
};
|
||||||
|
@ -239,7 +244,7 @@ namespace realm {
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ValueType, typename ContextType>
|
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>;
|
using Accessor = NativeAccessor<ValueType, ContextType>;
|
||||||
|
|
||||||
|
@ -279,7 +284,7 @@ namespace realm {
|
||||||
|
|
||||||
// populate
|
// populate
|
||||||
Object object(realm, object_schema, table->get(row_index));
|
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 (created || !prop.is_primary) {
|
||||||
if (Accessor::dict_has_value_for_key(ctx, value, prop.name)) {
|
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);
|
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>
|
template<typename ValueType, typename ContextType>
|
||||||
void List::add(ContextType ctx, ValueType value)
|
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>
|
template<typename ValueType, typename ContextType>
|
||||||
void List::insert(ContextType ctx, ValueType value, size_t list_ndx)
|
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>
|
template<typename ValueType, typename ContextType>
|
||||||
void List::set(ContextType ctx, ValueType value, size_t list_ndx)
|
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
|
#define REALM_FALLTHROUGH
|
||||||
#endif
|
#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_realm(std::move(r))
|
||||||
|
, m_object_schema(&o)
|
||||||
, m_query(std::move(q))
|
, m_query(std::move(q))
|
||||||
, m_table(m_query.get_table().get())
|
, m_table(m_query.get_table().get())
|
||||||
, m_sort(std::move(s))
|
, 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_realm(std::move(r))
|
||||||
|
, m_object_schema(&o)
|
||||||
, m_table(&table)
|
, m_table(&table)
|
||||||
, m_mode(Mode::Table)
|
, m_mode(Mode::Table)
|
||||||
{
|
{
|
||||||
|
@ -65,6 +67,17 @@ void Results::validate_write() const
|
||||||
throw InvalidTransactionException("Must be in a write transaction");
|
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()
|
size_t Results::size()
|
||||||
{
|
{
|
||||||
validate_read();
|
validate_read();
|
||||||
|
@ -92,7 +105,7 @@ RowExpr Results::get(size_t row_ndx)
|
||||||
case Mode::TableView:
|
case Mode::TableView:
|
||||||
update_tableview();
|
update_tableview();
|
||||||
if (row_ndx < m_table_view.size())
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,7 +159,9 @@ void Results::update_tableview()
|
||||||
m_mode = Mode::TableView;
|
m_mode = Mode::TableView;
|
||||||
break;
|
break;
|
||||||
case Mode::TableView:
|
case Mode::TableView:
|
||||||
m_table_view.sync_if_needed();
|
if (m_live) {
|
||||||
|
m_table_view.sync_if_needed();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -158,9 +173,10 @@ size_t Results::index_of(Row const& row)
|
||||||
throw DetatchedAccessorException{};
|
throw DetatchedAccessorException{};
|
||||||
}
|
}
|
||||||
if (m_table && row.get_table() != m_table) {
|
if (m_table && row.get_table() != m_table) {
|
||||||
throw IncorrectTableException{
|
throw IncorrectTableException(m_object_schema->name,
|
||||||
ObjectStore::object_type_for_table_name(m_table->get_name()),
|
ObjectStore::object_type_for_table_name(row.get_table()->get_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());
|
return index_of(row.get_index());
|
||||||
}
|
}
|
||||||
|
@ -310,23 +326,20 @@ TableView Results::get_tableview()
|
||||||
REALM_UNREACHABLE();
|
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
|
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
|
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) {
|
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_name = table->get_column_name(column);
|
, column_index(column)
|
||||||
column_type = table->get_column_type(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
|
// or a wrapper around a query and a sort order which creates and updates
|
||||||
// the tableview as needed
|
// the tableview as needed
|
||||||
Results() = default;
|
Results() = default;
|
||||||
Results(SharedRealm r, Table& table);
|
Results(SharedRealm r, const ObjectSchema& o, Table& table);
|
||||||
Results(SharedRealm r, Query q, SortOrder s = {});
|
Results(SharedRealm r, const ObjectSchema& o, Query q, SortOrder s = {});
|
||||||
|
|
||||||
// Results is copyable and moveable
|
// Results is copyable and moveable
|
||||||
Results(Results const&) = default;
|
Results(Results const&) = default;
|
||||||
Results(Results&&) = default;
|
Results(Results&&) = default;
|
||||||
Results& operator=(Results const&) = default;
|
|
||||||
Results& operator=(Results&&) = 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
|
// 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
|
// Returned query will not be valid if the current mode is Empty
|
||||||
Query get_query() const;
|
Query get_query() const;
|
||||||
|
@ -66,7 +72,10 @@ public:
|
||||||
TableView get_tableview();
|
TableView get_tableview();
|
||||||
|
|
||||||
// Get the object type which will be returned by get()
|
// 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
|
// Get the size of this results
|
||||||
// Can be either O(1) or O(N) depending on the state of things
|
// 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)
|
// The Results object has been invalidated (due to the Realm being invalidated)
|
||||||
// All non-noexcept functions can throw this
|
// 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
|
// The input index parameter was out of bounds
|
||||||
struct OutOfBoundsIndexException {
|
struct OutOfBoundsIndexException : public std::out_of_range
|
||||||
size_t requested;
|
{
|
||||||
size_t valid_count;
|
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
|
// 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
|
// The input Row object belongs to a different table
|
||||||
struct IncorrectTableException {
|
struct IncorrectTableException : public std::runtime_error {
|
||||||
StringData expected;
|
IncorrectTableException(StringData e, StringData a, const std::string &error) :
|
||||||
StringData actual;
|
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
|
// The requested aggregate operation is not supported for the column type
|
||||||
struct UnsupportedColumnTypeException {
|
struct UnsupportedColumnTypeException : public std::runtime_error {
|
||||||
size_t column_index;
|
size_t column_index;
|
||||||
StringData column_name;
|
StringData column_name;
|
||||||
DataType column_type;
|
DataType column_type;
|
||||||
|
@ -146,10 +167,12 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SharedRealm m_realm;
|
SharedRealm m_realm;
|
||||||
|
const ObjectSchema *m_object_schema;
|
||||||
Query m_query;
|
Query m_query;
|
||||||
TableView m_table_view;
|
TableView m_table_view;
|
||||||
Table* m_table = nullptr;
|
Table* m_table = nullptr;
|
||||||
SortOrder m_sort;
|
SortOrder m_sort;
|
||||||
|
bool m_live = true;
|
||||||
|
|
||||||
Mode m_mode = Mode::Empty;
|
Mode m_mode = Mode::Empty;
|
||||||
|
|
||||||
|
|
|
@ -402,8 +402,6 @@ uint64_t Realm::get_schema_version(const realm::Realm::Config &config)
|
||||||
|
|
||||||
void Realm::close()
|
void Realm::close()
|
||||||
{
|
{
|
||||||
invalidate();
|
|
||||||
|
|
||||||
if (m_notifier) {
|
if (m_notifier) {
|
||||||
m_notifier->remove_realm(this);
|
m_notifier->remove_realm(this);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue