diff --git a/object_accessor.hpp b/object_accessor.hpp index 5cc7a358..dfae7c16 100644 --- a/object_accessor.hpp +++ b/object_accessor.hpp @@ -76,6 +76,9 @@ namespace realm { static size_t to_object_index(ContextType ctx, SharedRealm realm, ValueType &val, const std::string &type, bool try_update); static ValueType from_object(ContextType ctx, Object); + // object index for an existing object + static size_t to_existing_object_index(ContextType ctx, ValueType &val); + // list value acessors static size_t list_size(ContextType ctx, ValueType &val); static ValueType list_value_at_index(ContextType ctx, ValueType &val, size_t index); diff --git a/object_store.hpp b/object_store.hpp index 60671f55..e038604c 100644 --- a/object_store.hpp +++ b/object_store.hpp @@ -19,6 +19,7 @@ #ifndef REALM_OBJECT_STORE_HPP #define REALM_OBJECT_STORE_HPP +#include "schema.hpp" #include "object_schema.hpp" #include "property.hpp" @@ -29,7 +30,6 @@ namespace realm { class ObjectSchemaValidationException; - class Schema; class ObjectStore { public: diff --git a/parser/query_builder.cpp b/parser/query_builder.cpp index 8f4aeeee..b924eaa3 100644 --- a/parser/query_builder.cpp +++ b/parser/query_builder.cpp @@ -62,7 +62,6 @@ struct FalseExpression : realm::Expression { // add a clause for numeric constraints based on operator type template void add_numeric_constraint_to_query(Query& query, - PropertyType datatype, Predicate::Operator operatorType, A lhs, B rhs) @@ -150,152 +149,176 @@ void add_string_constraint_to_query(realm::Query& query, } } -template -struct ColumnOfTypeHelper { - static Columns convert(TableGetter&& table, size_t idx) + +using KeyPath = std::vector; +KeyPath key_path_from_string(const std::string &s) { + std::stringstream ss(s); + std::string item; + KeyPath key_path; + while (std::getline(ss, item, '.')) { + key_path.push_back(item); + } + return key_path; +} + +struct PropertyExpression +{ + Property *prop = nullptr; + std::vector indexes; + std::function table_getter; + + PropertyExpression(Query &query, Schema &schema, ObjectSchema &desc, const std::string &key_path_string) { - return table()->template column(idx); + KeyPath key_path = key_path_from_string(key_path_string); + for (size_t index = 0; index < key_path.size(); index++) { + if (prop) { + precondition(prop->type == PropertyTypeObject || prop->type == PropertyTypeArray, + (std::string)"Property '" + key_path[index] + "' is not a link in object of type '" + desc.name + "'"); + indexes.push_back(prop->table_column); + + } + prop = desc.property_for_name(key_path[index]); + precondition(prop != nullptr, "No property '" + key_path[index] + "' on object of type '" + desc.name + "'"); + + if (prop->object_type.size()) { + desc = *schema.find(prop->object_type); + } + } + + table_getter = [&] { + TableRef& tbl = query.get_table(); + for (size_t col : indexes) { + tbl->link(col); // mutates m_link_chain on table + } + return tbl.get(); + }; + } +}; + +template +struct ColumnGetter { + static Columns convert(TableGetter&& table, const PropertyExpression & expr, Arguments &args) + { + return table()->template column(expr.prop->table_column); } }; template -struct ColumnOfTypeHelper { - static Columns convert(TableGetter&& table, size_t idx) +struct ColumnGetter { + static Columns convert(TableGetter&& table, const PropertyExpression & expr, Arguments &args) { - return table()->template column(idx); + return table()->template column(expr.prop->table_column); } }; template -struct ValueOfTypeHelper; +struct ValueGetter; template -struct ValueOfTypeHelper { - static Int convert(TableGetter&&, const parser::Expression & value) +struct ValueGetter { + static Int convert(TableGetter&&, const parser::Expression & value, Arguments &args) { - assert(0); + if (value.type != parser::Expression::Type::Argument) { + throw std::runtime_error("You must pass in a date argument to compare"); + } + DateTime dt = args.datetime_for_argument(std::stoi(value.s)); + return dt.get_datetime(); } }; template -struct ValueOfTypeHelper { - static bool convert(TableGetter&&, const parser::Expression & value) +struct ValueGetter { + static bool convert(TableGetter&&, const parser::Expression & value, Arguments &args) { + if (value.type == parser::Expression::Type::Argument) { + return args.bool_for_argument(std::stoi(value.s)); + } return value.type == parser::Expression::Type::True; } }; template -struct ValueOfTypeHelper { - static Double convert(TableGetter&&, const parser::Expression & value) +struct ValueGetter { + static Double convert(TableGetter&&, const parser::Expression & value, Arguments &args) { + if (value.type == parser::Expression::Type::Argument) { + return args.double_for_argument(std::stoi(value.s)); + } return std::stod(value.s); } }; template -struct ValueOfTypeHelper { - static Float convert(TableGetter&&, const parser::Expression & value) +struct ValueGetter { + static Float convert(TableGetter&&, const parser::Expression & value, Arguments &args) { + if (value.type == parser::Expression::Type::Argument) { + return args.float_for_argument(std::stoi(value.s)); + } return std::stof(value.s); } }; template -struct ValueOfTypeHelper { - static Int convert(TableGetter&&, const parser::Expression & value) +struct ValueGetter { + static Int convert(TableGetter&&, const parser::Expression & value, Arguments &args) { + if (value.type == parser::Expression::Type::Argument) { + return args.long_for_argument(std::stoi(value.s)); + } return std::stoll(value.s); } }; template -struct ValueOfTypeHelper { - static std::string convert(TableGetter&&, const parser::Expression & value) +struct ValueGetter { + static std::string convert(TableGetter&&, const parser::Expression & value, Arguments &args) { + if (value.type == parser::Expression::Type::Argument) { + return args.string_for_argument(std::stoi(value.s)); + } return value.s; } }; -template -auto value_of_type_for_query(TableGetter&& tables, Value&& value) +template +auto value_of_type_for_query(TableGetter&& tables, Value&& value, Arguments &args) { - const bool isColumnIndex = std::is_same::type>::value; - using helper = std::conditional_t, - ValueOfTypeHelper>; - return helper::convert(std::forward(tables), std::forward(value)); + const bool isColumn = std::is_same::type>::value; + using helper = std::conditional_t, ValueGetter>; + return helper::convert(std::forward(tables), std::forward(value), args); } -std::vector &split(const std::string &s, char delim, std::vector &elems) { - std::stringstream ss(s); - std::string item; - while (std::getline(ss, item, delim)) { - elems.push_back(item); - } - return elems; -} - -std::vector split(const std::string &s, char delim) { - std::vector elems; - split(s, delim, elems); - return elems; -} - -Property *get_property_from_key_path(Schema &schema, ObjectSchema &desc, const std::string &key_path, std::vector &indexes) +template +void do_add_comparison_to_query(Query &query, Schema &schema, ObjectSchema &object_schema, Predicate::Operator op, + PropertyExpression &expr, A &lhs, B &rhs, Arguments &args) { - Property *prop = nullptr; - - auto paths = split(key_path, '.'); - for (size_t index = 0; index < paths.size(); index++) { - if (prop) { - precondition(prop->type == PropertyTypeObject || prop->type == PropertyTypeArray, - (std::string)"Property '" + paths[index] + "' is not a link in object of type '" + desc.name + "'"); - indexes.push_back(prop->table_column); - - } - prop = desc.property_for_name(paths[index]); - precondition(prop != nullptr, "No property '" + paths[index] + "' on object of type '" + desc.name + "'"); - - if (prop->object_type.size()) { - desc = *schema.find(prop->object_type); - } - } - return prop; -} - -template -void do_add_comparison_to_query(Query &query, Schema &schema, ObjectSchema &object_schema, Property *prop, - Predicate::Operator op, const std::vector& indexes, T... values) -{ - auto table = [&] { - TableRef& tbl = query.get_table(); - for (size_t col : indexes) { - tbl->link(col); // mutates m_link_chain on table - } - return tbl.get(); - }; - - auto type = prop->type; + auto type = expr.prop->type; switch (type) { case PropertyTypeBool: - add_bool_constraint_to_query(query, op, value_of_type_for_query(table, values)...); + add_bool_constraint_to_query(query, op, value_of_type_for_query(expr.table_getter, lhs, args), + value_of_type_for_query(expr.table_getter, rhs, args)); break; case PropertyTypeDate: - add_numeric_constraint_to_query(query, type, op, value_of_type_for_query(table, values)...); + add_numeric_constraint_to_query(query, op, value_of_type_for_query(expr.table_getter, lhs, args), + value_of_type_for_query(expr.table_getter, rhs, args)); break; case PropertyTypeDouble: - add_numeric_constraint_to_query(query, type, op, value_of_type_for_query(table, values)...); + add_numeric_constraint_to_query(query, op, value_of_type_for_query(expr.table_getter, lhs, args), + value_of_type_for_query(expr.table_getter, rhs, args)); break; case PropertyTypeFloat: - add_numeric_constraint_to_query(query, type, op, value_of_type_for_query(table, values)...); + add_numeric_constraint_to_query(query, op, value_of_type_for_query(expr.table_getter, lhs, args), + value_of_type_for_query(expr.table_getter, rhs, args)); break; case PropertyTypeInt: - add_numeric_constraint_to_query(query, type, op, value_of_type_for_query(table, values)...); + add_numeric_constraint_to_query(query, op, value_of_type_for_query(expr.table_getter, lhs, args), + value_of_type_for_query(expr.table_getter, rhs, args)); break; case PropertyTypeString: case PropertyTypeData: - add_string_constraint_to_query(query, op, value_of_type_for_query(table, values)...); + add_string_constraint_to_query(query, op, value_of_type_for_query(expr.table_getter, lhs, args), + value_of_type_for_query(expr.table_getter, rhs, args)); break; default: { throw std::runtime_error((std::string)"Object type " + string_for_property_type(type) + " not supported"); @@ -303,25 +326,24 @@ void do_add_comparison_to_query(Query &query, Schema &schema, ObjectSchema &obje } } -void add_comparison_to_query(Query &query, Predicate &pred, Schema &schema, ObjectSchema &object_schema) +void add_comparison_to_query(Query &query, Predicate &pred, Arguments &args, Schema &schema, ObjectSchema &object_schema) { - std::vector indexes; Predicate::Comparison &cmpr = pred.cmpr; auto t0 = cmpr.expr[0].type, t1 = cmpr.expr[1].type; if (t0 == parser::Expression::Type::KeyPath && t1 != parser::Expression::Type::KeyPath) { - Property *prop = get_property_from_key_path(schema, object_schema, cmpr.expr[0].s, indexes); - do_add_comparison_to_query(query, schema, object_schema, prop, cmpr.op, indexes, prop->table_column, cmpr.expr[1]); + PropertyExpression expr(query, schema, object_schema, cmpr.expr[0].s); + do_add_comparison_to_query(query, schema, object_schema, cmpr.op, expr, expr, cmpr.expr[1], args); } else if (t0 != parser::Expression::Type::KeyPath && t1 == parser::Expression::Type::KeyPath) { - Property *prop = get_property_from_key_path(schema, object_schema, cmpr.expr[1].s, indexes); - do_add_comparison_to_query(query, schema, object_schema, prop, cmpr.op, indexes, cmpr.expr[0], prop->table_column); + PropertyExpression expr(query, schema, object_schema, cmpr.expr[1].s); + do_add_comparison_to_query(query, schema, object_schema, cmpr.op, expr, cmpr.expr[0], expr, args); } else { throw std::runtime_error("Predicate expressions must compare a keypath and another keypath or a constant value"); } } -void update_query_with_predicate(Query &query, Predicate &pred, Schema &schema, ObjectSchema &object_schema) +void update_query_with_predicate(Query &query, Predicate &pred, Arguments &arguments, Schema &schema, ObjectSchema &object_schema) { if (pred.negate) { query.Not(); @@ -331,7 +353,7 @@ void update_query_with_predicate(Query &query, Predicate &pred, Schema &schema, case Predicate::Type::And: query.group(); for (auto &sub : pred.cpnd.sub_predicates) { - update_query_with_predicate(query, sub, schema, object_schema); + update_query_with_predicate(query, sub, arguments, schema, object_schema); } if (!pred.cpnd.sub_predicates.size()) { query.and_query(new TrueExpression); @@ -343,7 +365,7 @@ void update_query_with_predicate(Query &query, Predicate &pred, Schema &schema, query.group(); for (auto &sub : pred.cpnd.sub_predicates) { query.Or(); - update_query_with_predicate(query, sub, schema, object_schema); + update_query_with_predicate(query, sub, arguments, schema, object_schema); } if (!pred.cpnd.sub_predicates.size()) { query.and_query(new FalseExpression); @@ -352,7 +374,7 @@ void update_query_with_predicate(Query &query, Predicate &pred, Schema &schema, break; case Predicate::Type::Comparison: { - add_comparison_to_query(query, pred, schema, object_schema); + add_comparison_to_query(query, pred, arguments, schema, object_schema); break; } case Predicate::Type::True: @@ -369,9 +391,9 @@ void update_query_with_predicate(Query &query, Predicate &pred, Schema &schema, } } -void apply_predicate(Query &query, Predicate &predicate, Schema &schema, std::string objectType) +void apply_predicate(Query &query, Predicate &predicate, Arguments &arguments, Schema &schema, std::string objectType) { - update_query_with_predicate(query, predicate, schema, *schema.find(objectType)); + update_query_with_predicate(query, predicate, arguments, schema, *schema.find(objectType)); // Test the constructed query in core std::string validateMessage = query.validate(); diff --git a/parser/query_builder.hpp b/parser/query_builder.hpp index 8a408ec8..1de648b3 100644 --- a/parser/query_builder.hpp +++ b/parser/query_builder.hpp @@ -21,13 +21,57 @@ #include #include "parser.hpp" +#include "object_accessor.hpp" namespace realm { class Query; class Schema; namespace query_builder { - void apply_predicate(Query &query, parser::Predicate &predicate, Schema &schema, std::string objectType); + class Arguments; + + void apply_predicate(Query &query, parser::Predicate &predicate, Arguments &arguments, Schema &schema, std::string objectType); + + class Arguments + { + public: + virtual bool bool_for_argument(size_t argument_index) = 0; + virtual long long long_for_argument(size_t argument_index) = 0; + virtual float float_for_argument(size_t argument_index) = 0; + virtual double double_for_argument(size_t argument_index) = 0; + virtual std::string string_for_argument(size_t argument_index) = 0; + virtual DateTime datetime_for_argument(size_t argument_index) = 0; + virtual size_t object_index_for_argument(size_t argument_index) = 0; + virtual bool is_argument_null(size_t argument_index) = 0; + }; + + template + class ArgumentConverter : public Arguments + { + public: + ArgumentConverter(ContextType context, std::vector arguments) : m_arguments(arguments), m_ctx(context) {}; + + using Accessor = realm::NativeAccessor; + virtual bool bool_for_argument(size_t argument_index) { return Accessor::to_bool(m_ctx, argument_at(argument_index)); } + virtual long long long_for_argument(size_t argument_index) { return Accessor::to_long(m_ctx, argument_at(argument_index)); } + virtual float float_for_argument(size_t argument_index) { return Accessor::to_float(m_ctx, argument_at(argument_index)); } + virtual double double_for_argument(size_t argument_index) { return Accessor::to_double(m_ctx, argument_at(argument_index)); } + virtual std::string string_for_argument(size_t argument_index) { return Accessor::to_string(m_ctx, argument_at(argument_index)); } + virtual DateTime datetime_for_argument(size_t argument_index) { return Accessor::to_datetime(m_ctx, argument_at(argument_index)); } + virtual size_t object_index_for_argument(size_t argument_index) { return Accessor::to_existing_object_index(m_ctx, argument_at(argument_index)); } + virtual bool is_argument_null(size_t argument_index) { return Accessor::is_null(m_ctx, argument_at(argument_index)); } + + private: + std::vector m_arguments; + ContextType m_ctx; + + ValueType &argument_at(size_t index) { + if (index >= m_arguments.size()) { + throw std::out_of_range((std::string)"Argument index " + std::to_string(index) + " out of range 0.." + std::to_string(m_arguments.size()-1)); + } + return m_arguments[index]; + } + }; } }