hook it up
This commit is contained in:
parent
d59e6b1f58
commit
1f78bf7db6
|
@ -24,6 +24,10 @@
|
|||
#include <pegtl/analyze.hh>
|
||||
#include <pegtl/trace.hh>
|
||||
|
||||
#include <realm.hpp>
|
||||
#include "object_store.hpp"
|
||||
#include "schema.hpp"
|
||||
|
||||
using namespace pegtl;
|
||||
|
||||
namespace realm {
|
||||
|
@ -291,9 +295,356 @@ Predicate parse(const std::string &query)
|
|||
state.predicate_stack.push_back(&out_predicate);
|
||||
|
||||
pegtl::parse< must< pred, eof >, action >(query, source, state);
|
||||
if (out_predicate.type == Predicate::Type::And && out_predicate.sub_predicates.size() == 1) {
|
||||
return out_predicate.sub_predicates.back();
|
||||
}
|
||||
return out_predicate;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// check a precondition and throw an exception if it is not met
|
||||
// this should be used iff the condition being false indicates a bug in the caller
|
||||
// of the function checking its preconditions
|
||||
static void precondition(bool condition, const std::string message) {
|
||||
if (__builtin_expect(condition, 1)) {
|
||||
return;
|
||||
}
|
||||
throw std::runtime_error(message);
|
||||
}
|
||||
|
||||
// FIXME: TrueExpression and FalseExpression should be supported by core in some way
|
||||
struct TrueExpression : realm::Expression {
|
||||
size_t find_first(size_t start, size_t end) const override
|
||||
{
|
||||
if (start != end)
|
||||
return start;
|
||||
|
||||
return not_found;
|
||||
}
|
||||
void set_table() override {}
|
||||
const Table* get_table() const override { return nullptr; }
|
||||
};
|
||||
|
||||
struct FalseExpression : realm::Expression {
|
||||
size_t find_first(size_t, size_t) const override { return not_found; }
|
||||
void set_table() override {}
|
||||
const Table* get_table() const override { return nullptr; }
|
||||
};
|
||||
|
||||
|
||||
// add a clause for numeric constraints based on operator type
|
||||
template <typename A, typename B>
|
||||
void add_numeric_constraint_to_query(Query& query,
|
||||
PropertyType datatype,
|
||||
Predicate::Operator operatorType,
|
||||
A lhs,
|
||||
B rhs)
|
||||
{
|
||||
switch (operatorType) {
|
||||
case Predicate::Operator::LessThan:
|
||||
query.and_query(lhs < rhs);
|
||||
break;
|
||||
case Predicate::Operator::LessThanOrEqual:
|
||||
query.and_query(lhs <= rhs);
|
||||
break;
|
||||
case Predicate::Operator::GreaterThan:
|
||||
query.and_query(lhs > rhs);
|
||||
break;
|
||||
case Predicate::Operator::GreaterThanOrEqual:
|
||||
query.and_query(lhs >= rhs);
|
||||
break;
|
||||
case Predicate::Operator::Equal:
|
||||
query.and_query(lhs == rhs);
|
||||
break;
|
||||
case Predicate::Operator::NotEqual:
|
||||
query.and_query(lhs != rhs);
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("Unsupported operator for numeric queries.");
|
||||
}
|
||||
}
|
||||
|
||||
template <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,
|
||||
StringData value) {
|
||||
bool case_sensitive = true;
|
||||
StringData sd = value;
|
||||
switch (op) {
|
||||
case Predicate::Operator::BeginsWith:
|
||||
query.and_query(column.begins_with(sd, case_sensitive));
|
||||
break;
|
||||
case Predicate::Operator::EndsWith:
|
||||
query.and_query(column.ends_with(sd, case_sensitive));
|
||||
break;
|
||||
case Predicate::Operator::Contains:
|
||||
query.and_query(column.contains(sd, case_sensitive));
|
||||
break;
|
||||
case Predicate::Operator::Equal:
|
||||
query.and_query(column.equal(sd, case_sensitive));
|
||||
break;
|
||||
case Predicate::Operator::NotEqual:
|
||||
query.and_query(column.not_equal(sd, case_sensitive));
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("Unsupported operator for string queries.");
|
||||
}
|
||||
}
|
||||
|
||||
void add_string_constraint_to_query(realm::Query& query,
|
||||
Predicate::Operator op,
|
||||
StringData value,
|
||||
Columns<String> &&column) {
|
||||
bool case_sensitive = true;
|
||||
StringData sd = value;
|
||||
switch (op) {
|
||||
case Predicate::Operator::Equal:
|
||||
query.and_query(column.equal(sd, case_sensitive));
|
||||
break;
|
||||
case Predicate::Operator::NotEqual:
|
||||
query.and_query(column.not_equal(sd, case_sensitive));
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("Substring comparison not supported for keypath substrings.");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename RequestedType, typename TableGetter>
|
||||
struct ColumnOfTypeHelper {
|
||||
static Columns<RequestedType> convert(TableGetter&& table, unsigned int idx)
|
||||
{
|
||||
return table()->template column<RequestedType>(idx);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename TableGetter>
|
||||
struct ColumnOfTypeHelper<DateTime, TableGetter> {
|
||||
static Columns<Int> convert(TableGetter&& table, unsigned int idx)
|
||||
{
|
||||
return table()->template column<Int>(idx);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename RequestedType, typename TableGetter>
|
||||
struct ValueOfTypeHelper;
|
||||
|
||||
template <typename TableGetter>
|
||||
struct ValueOfTypeHelper<DateTime, TableGetter> {
|
||||
static Int convert(TableGetter&&, const std::string & value)
|
||||
{
|
||||
assert(0);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename TableGetter>
|
||||
struct ValueOfTypeHelper<bool, TableGetter> {
|
||||
static bool convert(TableGetter&&, const std::string & value)
|
||||
{
|
||||
assert(0);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename TableGetter>
|
||||
struct ValueOfTypeHelper<Double, TableGetter> {
|
||||
static Double convert(TableGetter&&, const std::string & value)
|
||||
{
|
||||
return std::stod(value);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename TableGetter>
|
||||
struct ValueOfTypeHelper<Float, TableGetter> {
|
||||
static Float convert(TableGetter&&, const std::string & value)
|
||||
{
|
||||
return std::stof(value);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename TableGetter>
|
||||
struct ValueOfTypeHelper<Int, TableGetter> {
|
||||
static Int convert(TableGetter&&, const std::string & value)
|
||||
{
|
||||
return std::stoll(value);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename TableGetter>
|
||||
struct ValueOfTypeHelper<String, TableGetter> {
|
||||
static std::string convert(TableGetter&&, const std::string & value)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename RequestedType, typename Value, typename TableGetter>
|
||||
auto value_of_type_for_query(TableGetter&& tables, Value&& value)
|
||||
{
|
||||
const bool isColumnIndex = std::is_same<size_t, typename std::remove_reference<Value>::type>::value;
|
||||
using helper = std::conditional_t<isColumnIndex,
|
||||
ColumnOfTypeHelper<RequestedType, TableGetter>,
|
||||
ValueOfTypeHelper<RequestedType, TableGetter>>;
|
||||
return helper::convert(std::forward<TableGetter>(tables), std::forward<Value>(value));
|
||||
}
|
||||
|
||||
std::vector<std::string> &split(const std::string &s, char delim, std::vector<std::string> &elems) {
|
||||
std::stringstream ss(s);
|
||||
std::string item;
|
||||
while (std::getline(ss, item, delim)) {
|
||||
elems.push_back(item);
|
||||
}
|
||||
return elems;
|
||||
}
|
||||
|
||||
std::vector<std::string> split(const std::string &s, char delim) {
|
||||
std::vector<std::string> elems;
|
||||
split(s, delim, elems);
|
||||
return elems;
|
||||
}
|
||||
|
||||
Property *get_property_from_key_path(Schema &schema, ObjectSchema &desc, const std::string &key_path, std::vector<size_t> &indexes)
|
||||
{
|
||||
Property *prop = nullptr;
|
||||
|
||||
auto paths = split(key_path, '.');
|
||||
for (size_t index = 0; index < paths.size(); index++) {
|
||||
prop = desc.property_for_name(paths[index]);
|
||||
precondition(prop != nullptr, "No property '" + paths[index] + "' on object of type '" + desc.name + "'");
|
||||
precondition(index == paths.size() - 1 || prop->type == PropertyTypeObject || prop->type == PropertyTypeArray,
|
||||
(std::string)"Property '" + paths[index] + "' is not a link in object of type '" + desc.name + "'");
|
||||
|
||||
indexes.push_back(prop->table_column);
|
||||
desc = *schema.find(prop->object_type);
|
||||
}
|
||||
return prop;
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
void do_add_comparison_to_query(Query &query, Schema &schema, ObjectSchema &object_schema, Property *prop,
|
||||
Predicate::Operator op, const std::vector<size_t>& indexes, T... values)
|
||||
{
|
||||
auto table = [&] {
|
||||
TableRef& tbl = query.get_table();
|
||||
for (size_t col : indexes) {
|
||||
tbl->link(col); // mutates m_link_chain on table
|
||||
}
|
||||
return tbl.get();
|
||||
};
|
||||
|
||||
auto type = prop->type;
|
||||
switch (type) {
|
||||
case PropertyTypeBool:
|
||||
add_bool_constraint_to_query(query, op, value_of_type_for_query<bool>(table, values)...);
|
||||
break;
|
||||
case PropertyTypeDate:
|
||||
add_numeric_constraint_to_query(query, type, op, value_of_type_for_query<DateTime>(table, values)...);
|
||||
break;
|
||||
case PropertyTypeDouble:
|
||||
add_numeric_constraint_to_query(query, type, op, value_of_type_for_query<Double>(table, values)...);
|
||||
break;
|
||||
case PropertyTypeFloat:
|
||||
add_numeric_constraint_to_query(query, type, op, value_of_type_for_query<Float>(table, values)...);
|
||||
break;
|
||||
case PropertyTypeInt:
|
||||
add_numeric_constraint_to_query(query, type, op, value_of_type_for_query<Int>(table, values)...);
|
||||
break;
|
||||
case PropertyTypeString:
|
||||
case PropertyTypeData:
|
||||
add_string_constraint_to_query(query, op, value_of_type_for_query<String>(table, values)...);
|
||||
break;
|
||||
default: {
|
||||
throw std::runtime_error((std::string)"Object type " + string_for_property_type(type) + " not supported");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void add_comparison_to_query(Query &query, Predicate &pred, Schema &schema, ObjectSchema &object_schema)
|
||||
{
|
||||
std::vector<size_t> indexes;
|
||||
auto t0 = pred.sub_expressions[0].type, t1 = pred.sub_expressions[1].type;
|
||||
if (t0 == Expression::Type::KeyPath && t1 != Expression::Type::KeyPath) {
|
||||
Property *prop = get_property_from_key_path(schema, object_schema, pred.sub_expressions[0].s, indexes);
|
||||
do_add_comparison_to_query(query, schema, object_schema, prop, pred.op, indexes, prop->table_column, pred.sub_expressions[1].s);
|
||||
}
|
||||
else if (t0 != Expression::Type::KeyPath && t1 == Expression::Type::KeyPath) {
|
||||
Property *prop = get_property_from_key_path(schema, object_schema, pred.sub_expressions[1].s, indexes);
|
||||
do_add_comparison_to_query(query, schema, object_schema, prop, pred.op, indexes, pred.sub_expressions[0].s, prop->table_column);
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error("Predicate expressions must compare a keypath and another keypath or a constant value");
|
||||
}
|
||||
}
|
||||
|
||||
void update_query_with_predicate(Query &query, Predicate &pred, Schema &schema, ObjectSchema &object_schema)
|
||||
{
|
||||
if (pred.negate) {
|
||||
query.Not();
|
||||
}
|
||||
|
||||
switch (pred.type) {
|
||||
case Predicate::Type::And:
|
||||
query.group();
|
||||
for (auto &sub : pred.sub_predicates) {
|
||||
update_query_with_predicate(query, sub, schema, object_schema);
|
||||
}
|
||||
if (!pred.sub_predicates.size()) {
|
||||
query.and_query(new TrueExpression);
|
||||
}
|
||||
query.end_group();
|
||||
break;
|
||||
|
||||
case Predicate::Type::Or:
|
||||
query.group();
|
||||
for (auto &sub : pred.sub_predicates) {
|
||||
query.Or();
|
||||
update_query_with_predicate(query, sub, schema, object_schema);
|
||||
}
|
||||
if (!pred.sub_predicates.size()) {
|
||||
query.and_query(new FalseExpression);
|
||||
}
|
||||
query.end_group();
|
||||
break;
|
||||
|
||||
case Predicate::Type::Comparison: {
|
||||
add_comparison_to_query(query, pred, schema, object_schema);
|
||||
break;
|
||||
}
|
||||
case Predicate::Type::True:
|
||||
query.and_query(new TrueExpression);
|
||||
break;
|
||||
|
||||
case Predicate::Type::False:
|
||||
query.and_query(new FalseExpression);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw std::runtime_error("Invalid predicate type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void apply_predicate(Query &query, Predicate &predicate, Schema &schema, std::string objectType) {
|
||||
update_query_with_predicate(query, predicate, schema, *schema.find(objectType));
|
||||
|
||||
// Test the constructed query in core
|
||||
std::string validateMessage = query.validate();
|
||||
precondition(validateMessage.empty(), validateMessage.c_str());
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
|
||||
|
|
|
@ -23,6 +23,9 @@
|
|||
#include <string>
|
||||
|
||||
namespace realm {
|
||||
class Query;
|
||||
class Schema;
|
||||
|
||||
namespace parser {
|
||||
struct Expression
|
||||
{
|
||||
|
@ -74,6 +77,8 @@ namespace realm {
|
|||
};
|
||||
|
||||
Predicate parse(const std::string &query);
|
||||
|
||||
void apply_predicate(Query &query, Predicate &predicate, Schema &schema, std::string objectType);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ string = dq-string / sq-string
|
|||
sq-string = "'" *(%x20-ffffffff) "'"
|
||||
dq-string = DQUOTE *("\\" / %x20-21 / %x23-ffffffff) DQUOTE
|
||||
|
||||
num = float-num / int-num / hex-num
|
||||
float-num = ["-"] (*DIGIT "." 1*DIGIT / "." 1*DIGIT)
|
||||
int-num = ["-"] 1*DIGIT
|
||||
hex-num = ["-"] ["0"] "x" 1*HEXDIG
|
||||
num = ["-"] (float-num / int-num / hex-num)
|
||||
float-num = (*DIGIT "." 1*DIGIT / "." 1*DIGIT)
|
||||
int-num = 1*DIGIT
|
||||
hex-num = ("0x" / "0X") 1*HEXDIG
|
||||
|
|
Loading…
Reference in New Issue