Refactor TypedValue to PropertyValue
Summary: Rename TypedValue to PropertyValue. Move original TypedValue to query/backend/cpp. Fix undefined behaviours and memory leaks in PropertyValue. Add list type to PropertyValue. Operators and appropriate tests will be revised in following commits. Fix bugs in TypedValue Fix bugs in typed value Reviewers: buda, florijan Reviewed By: buda Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D77
This commit is contained in:
parent
b5274cd139
commit
ccdffa1d7e
@ -313,7 +313,7 @@ set(memgraph_src_files
|
||||
# ${src_dir}/snapshot/snapshoter.cpp
|
||||
# ${src_dir}/snapshot/snapshot_encoder.cpp
|
||||
# ${src_dir}/snapshot/snapshot_decoder.cpp
|
||||
${src_dir}/storage/typed_value.cpp
|
||||
${src_dir}/storage/property_value.cpp
|
||||
${src_dir}/storage/locking/record_lock.cpp
|
||||
# ${src_dir}/storage/garbage/garbage.cpp
|
||||
${src_dir}/storage/record_accessor.cpp
|
||||
@ -333,6 +333,7 @@ set(memgraph_src_files
|
||||
${src_dir}/database/graph_db.cpp
|
||||
${src_dir}/database/graph_db_accessor.cpp
|
||||
${src_dir}/query/backend/cpp/cypher_main_visitor.cpp
|
||||
${src_dir}/query/backend/cpp/typed_value.cpp
|
||||
)
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
|
@ -6,7 +6,8 @@
|
||||
#include "io/network/socket.hpp"
|
||||
|
||||
#include "database/graph_db.hpp"
|
||||
#include "storage/typed_value_store.hpp"
|
||||
#include "storage/property_value_store.hpp"
|
||||
#include <cassert>
|
||||
|
||||
template <class Stream>
|
||||
void bolt::BoltSerializer<Stream>::write(const VertexAccessor &vertex) {
|
||||
@ -27,13 +28,13 @@ void bolt::BoltSerializer<Stream>::write(const VertexAccessor &vertex) {
|
||||
encoder.write_string(vertex.db_accessor().label_name(label));
|
||||
|
||||
// write the properties
|
||||
const TypedValueStore<GraphDb::Property> &props = vertex.Properties();
|
||||
const PropertyValueStore<GraphDb::Property> &props = vertex.Properties();
|
||||
encoder.write_map_header(props.size());
|
||||
props.Accept(
|
||||
[this, &vertex](const GraphDb::Property prop, const TypedValue &value) {
|
||||
this->encoder.write(vertex.db_accessor().property_name(prop));
|
||||
this->write(value);
|
||||
});
|
||||
props.Accept([this, &vertex](const GraphDb::Property prop,
|
||||
const PropertyValue &value) {
|
||||
this->encoder.write(vertex.db_accessor().property_name(prop));
|
||||
this->write(value);
|
||||
});
|
||||
}
|
||||
|
||||
template <class Stream>
|
||||
@ -54,32 +55,36 @@ void bolt::BoltSerializer<Stream>::write(const EdgeAccessor &edge) {
|
||||
encoder.write(edge.db_accessor().edge_type_name(edge.edge_type()));
|
||||
|
||||
// write the property map
|
||||
const TypedValueStore<GraphDb::Property> &props = edge.Properties();
|
||||
const PropertyValueStore<GraphDb::Property> &props = edge.Properties();
|
||||
encoder.write_map_header(props.size());
|
||||
props.Accept([this, &edge](GraphDb::Property prop, const TypedValue &value) {
|
||||
this->encoder.write(edge.db_accessor().property_name(prop));
|
||||
this->write(value);
|
||||
});
|
||||
props.Accept(
|
||||
[this, &edge](GraphDb::Property prop, const PropertyValue &value) {
|
||||
this->encoder.write(edge.db_accessor().property_name(prop));
|
||||
this->write(value);
|
||||
});
|
||||
}
|
||||
|
||||
template <class Stream>
|
||||
void bolt::BoltSerializer<Stream>::write(const TypedValue &value) {
|
||||
switch (value.type_) {
|
||||
case TypedValue::Type::Null:
|
||||
void bolt::BoltSerializer<Stream>::write(const PropertyValue &value) {
|
||||
switch (value.type()) {
|
||||
case PropertyValue::Type::Null:
|
||||
encoder.write_null();
|
||||
return;
|
||||
case TypedValue::Type::Bool:
|
||||
case PropertyValue::Type::Bool:
|
||||
encoder.write_bool(value.Value<bool>());
|
||||
return;
|
||||
case TypedValue::Type::String:
|
||||
case PropertyValue::Type::String:
|
||||
encoder.write_string(value.Value<std::string>());
|
||||
return;
|
||||
case TypedValue::Type::Int:
|
||||
case PropertyValue::Type::Int:
|
||||
encoder.write_integer(value.Value<int>());
|
||||
return;
|
||||
case TypedValue::Type::Float:
|
||||
encoder.write_double(value.Value<float>());
|
||||
case PropertyValue::Type::Double:
|
||||
encoder.write_double(value.Value<double>());
|
||||
return;
|
||||
case PropertyValue::Type::List:
|
||||
// Not implemented
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
#include "storage/edge_accessor.hpp"
|
||||
#include "storage/vertex_accessor.hpp"
|
||||
|
||||
#include "storage/typed_value.hpp"
|
||||
#include "storage/property_value.hpp"
|
||||
|
||||
namespace bolt {
|
||||
|
||||
@ -46,11 +46,11 @@ class BoltSerializer {
|
||||
void write_failure(const std::map<std::string, std::string> &data);
|
||||
|
||||
/**
|
||||
* Writes a TypedValue (typically a property value in the edge or vertex).
|
||||
* Writes a PropertyValue (typically a property value in the edge or vertex).
|
||||
*
|
||||
* @param value The value to write.
|
||||
*/
|
||||
void write(const TypedValue &value);
|
||||
void write(const PropertyValue &value);
|
||||
|
||||
protected:
|
||||
Stream &encoder;
|
||||
|
@ -118,7 +118,7 @@ class RecordStream {
|
||||
chunk();
|
||||
}
|
||||
|
||||
void write(const TypedValue &value) { serializer.write(value); }
|
||||
void write(const PropertyValue &value) { serializer.write(value); }
|
||||
|
||||
void send() { chunked_buffer.flush(); }
|
||||
|
||||
|
@ -61,7 +61,7 @@ class RecordStreamMocker {
|
||||
void write(const EdgeAccessor &edge) {
|
||||
messages_.back().second.write(antlrcpp::Any(edge));
|
||||
}
|
||||
void write(const TypedValue &value) {
|
||||
void write(const PropertyValue &value) {
|
||||
messages_.back().second.write(antlrcpp::Any(value));
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "database/db_accessor.hpp"
|
||||
#include "storage/model/typed_value.hpp"
|
||||
#include "storage/model/typed_value_store.hpp"
|
||||
#include "storage/model/property_value.hpp"
|
||||
#include "storage/model/property_value_store.hpp"
|
||||
#include "storage/vertex_accessor.hpp"
|
||||
#include "utils/assert.hpp"
|
||||
|
||||
@ -95,6 +95,6 @@ class ElementSkeleton {
|
||||
Option<VertexAccessor> from_va;
|
||||
Option<EdgeType const *> type;
|
||||
std::vector<Label const *> labels;
|
||||
TypedValueStore properties_e;
|
||||
TypedValueStore properties_v;
|
||||
PropertyValueStore properties_e;
|
||||
PropertyValueStore properties_v;
|
||||
};
|
||||
|
517
src/query/backend/cpp/typed_value.cpp
Normal file
517
src/query/backend/cpp/typed_value.cpp
Normal file
@ -0,0 +1,517 @@
|
||||
#include "query/backend/cpp/typed_value.hpp"
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
#include "utils/assert.hpp"
|
||||
|
||||
TypedValue::TypedValue(const PropertyValue& value) {
|
||||
switch (value.type()) {
|
||||
case PropertyValue::Type::Null:
|
||||
type_ = Type::Null;
|
||||
return;
|
||||
|
||||
case PropertyValue::Type::Bool:
|
||||
type_ = Type::Bool;
|
||||
bool_v = value.Value<bool>();
|
||||
return;
|
||||
|
||||
case PropertyValue::Type::Int:
|
||||
type_ = Type::Int;
|
||||
int_v = value.Value<int>();
|
||||
return;
|
||||
|
||||
case PropertyValue::Type::Double:
|
||||
type_ = Type::Double;
|
||||
double_v = value.Value<double>();
|
||||
|
||||
case PropertyValue::Type::String:
|
||||
type_ = Type::String;
|
||||
new (&string_v) std::string(value.Value<std::string>());
|
||||
|
||||
case PropertyValue::Type::List:
|
||||
type_ = Type::List;
|
||||
auto vec = value.Value<std::vector<PropertyValue>>();
|
||||
new (&list_v) std::vector<TypedValue>(vec.begin(), vec.end());
|
||||
return;
|
||||
}
|
||||
|
||||
permanent_fail("Unsupported PropertyValue::Type");
|
||||
}
|
||||
|
||||
TypedValue::operator PropertyValue() const {
|
||||
switch (type_) {
|
||||
case TypedValue::Type::Null:
|
||||
return PropertyValue::Null;
|
||||
case TypedValue::Type::Bool:
|
||||
return PropertyValue(bool_v);
|
||||
case TypedValue::Type::Int:
|
||||
return PropertyValue(int_v);
|
||||
case TypedValue::Type::Double:
|
||||
return PropertyValue(double_v);
|
||||
case TypedValue::Type::String:
|
||||
return PropertyValue(string_v);
|
||||
case TypedValue::Type::List:
|
||||
return PropertyValue(
|
||||
std::vector<PropertyValue>(list_v.begin(), list_v.end()));
|
||||
}
|
||||
permanent_fail("Unsupported PropertyValue::Type");
|
||||
}
|
||||
|
||||
// Value extraction template instantiations
|
||||
template <>
|
||||
bool TypedValue::Value<bool>() const {
|
||||
debug_assert(type_ == TypedValue::Type::Bool,
|
||||
"Incompatible template param and type");
|
||||
return bool_v;
|
||||
}
|
||||
|
||||
template <>
|
||||
std::string TypedValue::Value<std::string>() const {
|
||||
debug_assert(type_ == TypedValue::Type::String,
|
||||
"Incompatible template param and type");
|
||||
return string_v;
|
||||
}
|
||||
|
||||
template <>
|
||||
int TypedValue::Value<int>() const {
|
||||
debug_assert(type_ == TypedValue::Type::Int,
|
||||
"Incompatible template param and type");
|
||||
return int_v;
|
||||
}
|
||||
|
||||
template <>
|
||||
double TypedValue::Value<double>() const {
|
||||
debug_assert(type_ == TypedValue::Type::Double,
|
||||
"Incompatible template param and type");
|
||||
return double_v;
|
||||
}
|
||||
|
||||
template <>
|
||||
std::vector<TypedValue> TypedValue::Value<std::vector<TypedValue>>() const {
|
||||
debug_assert(type_ == TypedValue::Type::List,
|
||||
"Incompatible template param and type");
|
||||
return list_v;
|
||||
}
|
||||
|
||||
TypedValue::TypedValue(const TypedValue& other) : type_(other.type_) {
|
||||
switch (other.type_) {
|
||||
case TypedValue::Type::Null:
|
||||
return;
|
||||
|
||||
case TypedValue::Type::Bool:
|
||||
this->bool_v = other.bool_v;
|
||||
return;
|
||||
|
||||
case TypedValue::Type::String:
|
||||
new (&string_v) std::string(other.string_v);
|
||||
return;
|
||||
|
||||
case Type::Int:
|
||||
this->int_v = other.int_v;
|
||||
return;
|
||||
|
||||
case Type::Double:
|
||||
this->double_v = other.double_v;
|
||||
return;
|
||||
|
||||
case Type::List:
|
||||
new (&list_v) std::vector<TypedValue>(other.list_v);
|
||||
return;
|
||||
}
|
||||
|
||||
permanent_fail("Unsupported TypedValue::Type");
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const TypedValue::Type type) {
|
||||
switch (type) {
|
||||
case TypedValue::Type::Null:
|
||||
return os << "null";
|
||||
case TypedValue::Type::Bool:
|
||||
return os << "bool";
|
||||
case TypedValue::Type::String:
|
||||
return os << "string";
|
||||
case TypedValue::Type::Int:
|
||||
return os << "int";
|
||||
case TypedValue::Type::Double:
|
||||
return os << "double";
|
||||
case TypedValue::Type::List:
|
||||
return os << "list";
|
||||
}
|
||||
permanent_fail("Unsupported TypedValue::Type");
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const TypedValue& value) {
|
||||
switch (value.type_) {
|
||||
case TypedValue::Type::Null:
|
||||
return os << "Null";
|
||||
case TypedValue::Type::Bool:
|
||||
return os << (value.Value<bool>() ? "true" : "false");
|
||||
case TypedValue::Type::String:
|
||||
return os << value.Value<std::string>();
|
||||
case TypedValue::Type::Int:
|
||||
return os << value.Value<int>();
|
||||
case TypedValue::Type::Double:
|
||||
return os << value.Value<double>();
|
||||
case TypedValue::Type::List:
|
||||
os << "[";
|
||||
for (const auto& x : value.Value<std::vector<TypedValue>>()) {
|
||||
os << x << ",";
|
||||
}
|
||||
return os << "]";
|
||||
}
|
||||
permanent_fail("Unsupported PropertyValue::Type");
|
||||
}
|
||||
|
||||
TypedValue& TypedValue::operator=(const TypedValue& other) {
|
||||
// set the type of this
|
||||
this->~TypedValue();
|
||||
type_ = other.type_;
|
||||
|
||||
if (this != &other) {
|
||||
switch (other.type_) {
|
||||
case TypedValue::Type::Null:
|
||||
case TypedValue::Type::Bool:
|
||||
this->bool_v = other.bool_v;
|
||||
return *this;
|
||||
case TypedValue::Type::String:
|
||||
new (&string_v) std::string(other.string_v);
|
||||
return *this;
|
||||
case TypedValue::Type::Int:
|
||||
this->int_v = other.int_v;
|
||||
return *this;
|
||||
case TypedValue::Type::Double:
|
||||
this->double_v = other.double_v;
|
||||
return *this;
|
||||
case TypedValue::Type::List:
|
||||
new (&list_v) std::vector<TypedValue>(other.list_v);
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
permanent_fail("Unsupported TypedValue::Type");
|
||||
}
|
||||
|
||||
const TypedValue TypedValue::Null = TypedValue();
|
||||
|
||||
TypedValue::~TypedValue() {
|
||||
switch (type_) {
|
||||
// destructor for primitive types does nothing
|
||||
case Type::Null:
|
||||
case Type::Bool:
|
||||
case Type::Int:
|
||||
case Type::Double:
|
||||
return;
|
||||
|
||||
// we need to call destructors for non primitive types since we used
|
||||
// placement new
|
||||
case Type::String:
|
||||
// Clang fails to compile ~std::string. It seems it is a bug in some
|
||||
// versions of clang. using namespace std statement solves the issue.
|
||||
using namespace std;
|
||||
string_v.~string();
|
||||
return;
|
||||
case Type::List:
|
||||
using namespace std;
|
||||
list_v.~vector<TypedValue>();
|
||||
return;
|
||||
}
|
||||
permanent_fail("Unsupported TypedValue::Type");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the double value of a value.
|
||||
* The value MUST be either Double or Int.
|
||||
*
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
double ToDouble(const TypedValue& value) {
|
||||
switch (value.type()) {
|
||||
case TypedValue::Type::Int:
|
||||
return (double)value.Value<int>();
|
||||
case TypedValue::Type::Double:
|
||||
return value.Value<double>();
|
||||
|
||||
default:
|
||||
permanent_fail("Unsupported TypedValue::Type");
|
||||
}
|
||||
}
|
||||
|
||||
TypedValue operator<(const TypedValue& a, const TypedValue& b) {
|
||||
if (a.type() == TypedValue::Type::Bool || b.type() == TypedValue::Type::Bool)
|
||||
throw TypedValueException("Invalid 'less' operand types({} + {})", a.type(),
|
||||
b.type());
|
||||
|
||||
if (a.type() == TypedValue::Type::Null || b.type() == TypedValue::Type::Null)
|
||||
return TypedValue::Null;
|
||||
|
||||
if (a.type() == TypedValue::Type::String ||
|
||||
b.type() == TypedValue::Type::String) {
|
||||
if (a.type() != b.type())
|
||||
throw TypedValueException("Invalid equality operand types({} + {})",
|
||||
a.type(), b.type());
|
||||
else
|
||||
return a.Value<std::string>() < b.Value<std::string>();
|
||||
}
|
||||
|
||||
// at this point we only have int and double
|
||||
if (a.type() == TypedValue::Type::Double ||
|
||||
b.type() == TypedValue::Type::Double)
|
||||
return ToDouble(a) < ToDouble(b);
|
||||
else
|
||||
return a.Value<int>() < b.Value<int>();
|
||||
}
|
||||
|
||||
// TODO: 2 = "2" -> false, I don't this is handled correctly at the moment.
|
||||
TypedValue operator==(const TypedValue& a, const TypedValue& b) {
|
||||
if (a.type() == TypedValue::Type::Null || b.type() == TypedValue::Type::Null)
|
||||
return TypedValue::Null;
|
||||
|
||||
if (a.type() == TypedValue::Type::List ||
|
||||
b.type() == TypedValue::Type::List) {
|
||||
if (a.type() == TypedValue::Type::List &&
|
||||
b.type() == TypedValue::Type::List) {
|
||||
// Potential optimisation: There is no need to copies of both lists to
|
||||
// compare them. If operator becomes a friend of TypedValue class then we
|
||||
// can compare list_v-s directly.
|
||||
auto list1 = a.Value<std::vector<TypedValue>>();
|
||||
auto list2 = b.Value<std::vector<TypedValue>>();
|
||||
if (list1.size() != list2.size()) return false;
|
||||
for (int i = 0; i < (int)list1.size(); ++i) {
|
||||
if (!(list1[i] == list2[i]).Value<bool>()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// We are not compatible with neo4j at this point. In neo4j 2 = [2] compares
|
||||
// to true. That is not the end of unselfishness of developers at neo4j so
|
||||
// they allow us to use as many braces as we want to get to the truth in
|
||||
// list comparison, so [[2]] = [[[[[[2]]]]]] compares to true in neo4j as
|
||||
// well. Because, why not?
|
||||
// At memgraph we prefer sanity so [1,2] = [1,2] compares to true and
|
||||
// 2 = [2] compares to false.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (a.type() == TypedValue::Type::String ||
|
||||
b.type() == TypedValue::Type::String) {
|
||||
if (a.type() != b.type())
|
||||
throw TypedValueException("Invalid equality operand types({} + {})",
|
||||
a.type(), b.type());
|
||||
else
|
||||
return a.Value<std::string>() == b.Value<std::string>();
|
||||
}
|
||||
|
||||
if (a.type() == TypedValue::Type::Bool ||
|
||||
b.type() == TypedValue::Type::Bool) {
|
||||
if (a.type() != b.type())
|
||||
throw TypedValueException("Invalid equality operand types({} + {})",
|
||||
a.type(), b.type());
|
||||
else
|
||||
return a.Value<bool>() == b.Value<bool>();
|
||||
}
|
||||
// at this point we only have int and double
|
||||
if (a.type() == TypedValue::Type::Double ||
|
||||
b.type() == TypedValue::Type::Double) {
|
||||
return ToDouble(a) == ToDouble(b);
|
||||
} else
|
||||
return a.Value<int>() == b.Value<int>();
|
||||
}
|
||||
|
||||
TypedValue operator!(const TypedValue& a) {
|
||||
switch (a.type()) {
|
||||
case TypedValue::Type::Null:
|
||||
return TypedValue::Null;
|
||||
case TypedValue::Type::Bool:
|
||||
return TypedValue(!a.Value<bool>());
|
||||
|
||||
default:
|
||||
throw TypedValueException("Invalid logical not operand type (!{})",
|
||||
a.type());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns a numeric or string value into a string.
|
||||
*
|
||||
* @param value a value.
|
||||
* @return A string.
|
||||
*/
|
||||
std::string ValueToString(const TypedValue& value) {
|
||||
switch (value.type()) {
|
||||
case TypedValue::Type::String:
|
||||
return value.Value<std::string>();
|
||||
case TypedValue::Type::Int:
|
||||
return std::to_string(value.Value<int>());
|
||||
case TypedValue::Type::Double:
|
||||
return fmt::format("{}", value.Value<double>());
|
||||
|
||||
// unsupported situations
|
||||
default:
|
||||
permanent_fail("Unsupported TypedValue::Type");
|
||||
}
|
||||
}
|
||||
|
||||
TypedValue operator-(const TypedValue& a) {
|
||||
switch (a.type()) {
|
||||
case TypedValue::Type::Null:
|
||||
return TypedValue::Null;
|
||||
case TypedValue::Type::Int:
|
||||
return -a.Value<int>();
|
||||
case TypedValue::Type::Double:
|
||||
return -a.Value<double>();
|
||||
|
||||
default:
|
||||
throw TypedValueException("Invalid unary minus operand type (-{})",
|
||||
a.type());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Raises a TypedValueException if the given values do not support arithmetic
|
||||
* operations. If they do, nothing happens.
|
||||
*
|
||||
* @param a First value.
|
||||
* @param b Second value.
|
||||
* @param string_ok If or not for the given operation it's valid to work with
|
||||
* String values (typically it's OK only for sum).
|
||||
* @param op_name Name of the operation, used only for exception description,
|
||||
* if raised.
|
||||
*/
|
||||
inline void EnsureArithmeticallyOk(const TypedValue& a, const TypedValue& b,
|
||||
bool string_ok, const std::string& op_name) {
|
||||
if (a.type() == TypedValue::Type::Bool || b.type() == TypedValue::Type::Bool)
|
||||
throw TypedValueException("Invalid {} operand types {}, {}", op_name,
|
||||
a.type(), b.type());
|
||||
|
||||
if (string_ok) return;
|
||||
|
||||
if (a.type() == TypedValue::Type::String ||
|
||||
b.type() == TypedValue::Type::String)
|
||||
throw TypedValueException("Invalid subtraction operands types {}, {}",
|
||||
a.type(), b.type());
|
||||
}
|
||||
|
||||
TypedValue operator+(const TypedValue& a, const TypedValue& b) {
|
||||
if (a.type() == TypedValue::Type::Null || b.type() == TypedValue::Type::Null)
|
||||
return TypedValue::Null;
|
||||
|
||||
if (a.type() == TypedValue::Type::List ||
|
||||
b.type() == TypedValue::Type::List) {
|
||||
std::vector<TypedValue> list;
|
||||
auto append_list = [&list](const TypedValue& v) {
|
||||
if (v.type() == TypedValue::Type::List) {
|
||||
auto list2 = v.Value<std::vector<TypedValue>>();
|
||||
list.insert(list.end(), list2.begin(), list2.end());
|
||||
} else {
|
||||
list.push_back(v);
|
||||
}
|
||||
};
|
||||
append_list(a);
|
||||
append_list(b);
|
||||
return TypedValue(list);
|
||||
}
|
||||
|
||||
EnsureArithmeticallyOk(a, b, true, "addition");
|
||||
// no more Bool nor Null, summing works on anything from here onward
|
||||
|
||||
if (a.type() == TypedValue::Type::String ||
|
||||
b.type() == TypedValue::Type::String)
|
||||
return ValueToString(a) + ValueToString(b);
|
||||
|
||||
// at this point we only have int and double
|
||||
if (a.type() == TypedValue::Type::Double ||
|
||||
b.type() == TypedValue::Type::Double) {
|
||||
return ToDouble(a) + ToDouble(b);
|
||||
} else
|
||||
return a.Value<int>() + b.Value<int>();
|
||||
}
|
||||
|
||||
TypedValue operator-(const TypedValue& a, const TypedValue& b) {
|
||||
EnsureArithmeticallyOk(a, b, false, "subtraction");
|
||||
|
||||
if (a.type() == TypedValue::Type::Null || b.type() == TypedValue::Type::Null)
|
||||
return TypedValue::Null;
|
||||
|
||||
// at this point we only have int and double
|
||||
if (a.type() == TypedValue::Type::Double ||
|
||||
b.type() == TypedValue::Type::Double) {
|
||||
return ToDouble(a) - ToDouble(b);
|
||||
} else
|
||||
return a.Value<int>() - b.Value<int>();
|
||||
}
|
||||
|
||||
TypedValue operator/(const TypedValue& a, const TypedValue& b) {
|
||||
EnsureArithmeticallyOk(a, b, false, "division");
|
||||
|
||||
if (a.type() == TypedValue::Type::Null || b.type() == TypedValue::Type::Null)
|
||||
return TypedValue::Null;
|
||||
|
||||
// at this point we only have int and double
|
||||
if (a.type() == TypedValue::Type::Double ||
|
||||
b.type() == TypedValue::Type::Double) {
|
||||
return ToDouble(a) / ToDouble(b);
|
||||
} else
|
||||
return a.Value<int>() / b.Value<int>();
|
||||
}
|
||||
|
||||
TypedValue operator*(const TypedValue& a, const TypedValue& b) {
|
||||
EnsureArithmeticallyOk(a, b, false, "multiplication");
|
||||
|
||||
if (a.type() == TypedValue::Type::Null || b.type() == TypedValue::Type::Null)
|
||||
return TypedValue::Null;
|
||||
|
||||
// at this point we only have int and double
|
||||
if (a.type() == TypedValue::Type::Double ||
|
||||
b.type() == TypedValue::Type::Double) {
|
||||
return ToDouble(a) * ToDouble(b);
|
||||
} else
|
||||
return a.Value<int>() * b.Value<int>();
|
||||
}
|
||||
|
||||
TypedValue operator%(const TypedValue& a, const TypedValue& b) {
|
||||
EnsureArithmeticallyOk(a, b, false, "modulo");
|
||||
|
||||
if (a.type() == TypedValue::Type::Null || b.type() == TypedValue::Type::Null)
|
||||
return TypedValue::Null;
|
||||
|
||||
// at this point we only have int and double
|
||||
if (a.type() == TypedValue::Type::Double ||
|
||||
b.type() == TypedValue::Type::Double) {
|
||||
return (double)fmod(ToDouble(a), ToDouble(b));
|
||||
} else
|
||||
return a.Value<int>() % b.Value<int>();
|
||||
}
|
||||
|
||||
inline bool IsLogicallyOk(const TypedValue& a) {
|
||||
return a.type() == TypedValue::Type::Bool ||
|
||||
a.type() == TypedValue::Type::Null;
|
||||
}
|
||||
|
||||
// TODO: Fix bugs in && and ||. null or true -> true; false and null -> false
|
||||
TypedValue operator&&(const TypedValue& a, const TypedValue& b) {
|
||||
if (IsLogicallyOk(a) && IsLogicallyOk(b)) {
|
||||
if (a.type() == TypedValue::Type::Null ||
|
||||
b.type() == TypedValue::Type::Null)
|
||||
return TypedValue::Null;
|
||||
else
|
||||
return a.Value<bool>() && b.Value<bool>();
|
||||
} else
|
||||
throw TypedValueException("Invalid logical and operand types({} && {})",
|
||||
a.type(), b.type());
|
||||
}
|
||||
|
||||
TypedValue operator||(const TypedValue& a, const TypedValue& b) {
|
||||
if (IsLogicallyOk(a) && IsLogicallyOk(b)) {
|
||||
if (a.type() == TypedValue::Type::Null ||
|
||||
b.type() == TypedValue::Type::Null)
|
||||
return TypedValue::Null;
|
||||
else
|
||||
return a.Value<bool>() || b.Value<bool>();
|
||||
} else
|
||||
throw TypedValueException("Invalid logical and operand types({} && {})",
|
||||
a.type(), b.type());
|
||||
}
|
@ -8,6 +8,7 @@
|
||||
#include "utils/exceptions/stacktrace_exception.hpp"
|
||||
#include "utils/total_ordering.hpp"
|
||||
#include "utils/underlying_cast.hpp"
|
||||
#include "storage/property_value.hpp"
|
||||
|
||||
/**
|
||||
* Encapsulation of a value and it's type encapsulated in a class that has no
|
||||
@ -23,7 +24,7 @@ class TypedValue : public TotalOrdering<TypedValue, TypedValue, TypedValue> {
|
||||
|
||||
public:
|
||||
/** A value type. Each type corresponds to exactly one C++ type */
|
||||
enum class Type : unsigned { Null, String, Bool, Int, Float };
|
||||
enum class Type : unsigned { Null, String, Bool, Int, Double, List };
|
||||
|
||||
// single static reference to Null, used whenever Null should be returned
|
||||
static const TypedValue Null;
|
||||
@ -31,23 +32,30 @@ class TypedValue : public TotalOrdering<TypedValue, TypedValue, TypedValue> {
|
||||
// constructors for primitive types
|
||||
TypedValue(bool value) : type_(Type::Bool) { bool_v = value; }
|
||||
TypedValue(int value) : type_(Type::Int) { int_v = value; }
|
||||
TypedValue(float value) : type_(Type::Float) { float_v = value; }
|
||||
TypedValue(double value) : type_(Type::Double) { double_v = value; }
|
||||
|
||||
// conversion function to PropertyValue
|
||||
operator PropertyValue() const;
|
||||
|
||||
/// constructors for non-primitive types (shared pointers)
|
||||
TypedValue(const std::string& value) : type_(Type::String) {
|
||||
new (&string_v) std::shared_ptr<std::string>(new std::string(value));
|
||||
new (&string_v) std::string(value);
|
||||
}
|
||||
TypedValue(const char* value) : type_(Type::String) {
|
||||
new (&string_v) std::shared_ptr<std::string>(new std::string(value));
|
||||
new (&string_v) std::string(value);
|
||||
}
|
||||
TypedValue(const std::vector<TypedValue>& value) : type_(Type::List) {
|
||||
new (&list_v) std::vector<TypedValue>(value);
|
||||
}
|
||||
TypedValue(const PropertyValue& value);
|
||||
|
||||
// assignment ops
|
||||
TypedValue& operator=(TypedValue& other);
|
||||
TypedValue& operator=(TypedValue&& other);
|
||||
TypedValue& operator=(const TypedValue& other);
|
||||
|
||||
TypedValue(const TypedValue& other);
|
||||
~TypedValue();
|
||||
|
||||
Type type() const { return type_; }
|
||||
/**
|
||||
* Returns the value of the property as given type T.
|
||||
* The behavior of this function is undefined if
|
||||
@ -61,19 +69,26 @@ class TypedValue : public TotalOrdering<TypedValue, TypedValue, TypedValue> {
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& stream, const TypedValue& prop);
|
||||
|
||||
/**
|
||||
* The Type of property.
|
||||
*/
|
||||
const Type type_;
|
||||
|
||||
private:
|
||||
// storage for the value of the property
|
||||
union {
|
||||
bool bool_v;
|
||||
int int_v;
|
||||
float float_v;
|
||||
std::shared_ptr<std::string> string_v;
|
||||
double double_v;
|
||||
// Since this is used in query runtime, size of union is not critical so
|
||||
// string and vector are used instead of pointers. It requires copy of data,
|
||||
// but most of algorithms (concatenations, serialisation...) has linear time
|
||||
// complexity so it shouldn't be a problem. This is maybe even faster
|
||||
// because of data locality.
|
||||
std::string string_v;
|
||||
std::vector<TypedValue> list_v;
|
||||
// Node, Edge, Path, Map missing.
|
||||
};
|
||||
|
||||
/**
|
||||
* The Type of property.
|
||||
*/
|
||||
Type type_;
|
||||
};
|
||||
|
||||
/**
|
@ -24,7 +24,7 @@ class PlanInterface {
|
||||
*
|
||||
* @return bool status after execution (success OR fail)
|
||||
*/
|
||||
virtual bool run(GraphDbAccessor &db_accessor, const TypedValueStore<> &args,
|
||||
virtual bool run(GraphDbAccessor &db_accessor, const PropertyValueStore<> &args,
|
||||
Stream &stream) = 0;
|
||||
|
||||
/**
|
||||
|
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "storage/typed_value_store.hpp"
|
||||
#include "storage/property_value_store.hpp"
|
||||
#include "utils/hashing/fnv.hpp"
|
||||
|
||||
/*
|
||||
@ -10,10 +10,10 @@
|
||||
* * hash of stripped query
|
||||
*/
|
||||
struct StrippedQuery {
|
||||
StrippedQuery(const std::string &&query, TypedValueStore<> &&arguments,
|
||||
StrippedQuery(const std::string &&query, PropertyValueStore<> &&arguments,
|
||||
HashType hash)
|
||||
: query(std::forward<const std::string>(query)),
|
||||
arguments(std::forward<TypedValueStore<>>(arguments)),
|
||||
arguments(std::forward<PropertyValueStore<>>(arguments)),
|
||||
hash(hash) {}
|
||||
|
||||
/**
|
||||
@ -38,7 +38,7 @@ struct StrippedQuery {
|
||||
/**
|
||||
* Stripped arguments
|
||||
*/
|
||||
const TypedValueStore<> arguments;
|
||||
const PropertyValueStore<> arguments;
|
||||
|
||||
/**
|
||||
* Hash based on stripped query.
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
#include "logging/loggable.hpp"
|
||||
#include "query/stripped.hpp"
|
||||
#include "storage/typed_value_store.hpp"
|
||||
#include "storage/property_value_store.hpp"
|
||||
#include "utils/hashing/fnv.hpp"
|
||||
#include "utils/string/transform.hpp"
|
||||
#include "utils/variadic/variadic.hpp"
|
||||
@ -54,7 +54,7 @@ class QueryStripper : public Loggable {
|
||||
CommonTokenStream tokens(&lexer);
|
||||
|
||||
// initialize data structures that will be populated
|
||||
TypedValueStore<> stripped_arguments;
|
||||
PropertyValueStore<> stripped_arguments;
|
||||
std::string stripped_query;
|
||||
stripped_query.reserve(query.size());
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
#include "database/graph_db.hpp"
|
||||
#include "mvcc/record.hpp"
|
||||
#include "mvcc/version_list.hpp"
|
||||
#include "storage/typed_value_store.hpp"
|
||||
#include "storage/property_value_store.hpp"
|
||||
|
||||
// forward declare Vertex because there is a circular usage Edge <-> Vertex
|
||||
class Vertex;
|
||||
@ -17,5 +17,5 @@ class Edge : public mvcc::Record<Edge> {
|
||||
mvcc::VersionList<Vertex>& from_;
|
||||
mvcc::VersionList<Vertex>& to_;
|
||||
GraphDb::EdgeType edge_type_;
|
||||
TypedValueStore<GraphDb::Property> properties_;
|
||||
PropertyValueStore<GraphDb::Property> properties_;
|
||||
};
|
||||
|
163
src/storage/property_value.cpp
Normal file
163
src/storage/property_value.cpp
Normal file
@ -0,0 +1,163 @@
|
||||
#include "storage/property_value.hpp"
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
#include "utils/assert.hpp"
|
||||
|
||||
// Value extraction template instantiations
|
||||
template <>
|
||||
bool PropertyValue::Value<bool>() const {
|
||||
debug_assert(type_ == PropertyValue::Type::Bool,
|
||||
"Incompatible template param and type");
|
||||
return bool_v;
|
||||
}
|
||||
|
||||
template <>
|
||||
std::string PropertyValue::Value<std::string>() const {
|
||||
debug_assert(type_ == PropertyValue::Type::String,
|
||||
"Incompatible template param and type");
|
||||
return *string_v;
|
||||
}
|
||||
|
||||
template <>
|
||||
int PropertyValue::Value<int>() const {
|
||||
debug_assert(type_ == PropertyValue::Type::Int,
|
||||
"Incompatible template param and type");
|
||||
return int_v;
|
||||
}
|
||||
|
||||
template <>
|
||||
double PropertyValue::Value<double>() const {
|
||||
debug_assert(type_ == PropertyValue::Type::Double,
|
||||
"Incompatible template param and type");
|
||||
return double_v;
|
||||
}
|
||||
|
||||
template <>
|
||||
std::vector<PropertyValue> PropertyValue::Value<std::vector<PropertyValue>>()
|
||||
const {
|
||||
debug_assert(type_ == PropertyValue::Type::List,
|
||||
"Incompatible template param and type");
|
||||
return *list_v;
|
||||
}
|
||||
|
||||
PropertyValue::PropertyValue(const PropertyValue& other) : type_(other.type_) {
|
||||
switch (other.type_) {
|
||||
case PropertyValue::Type::Null:
|
||||
return;
|
||||
|
||||
case PropertyValue::Type::Bool:
|
||||
this->bool_v = other.bool_v;
|
||||
return;
|
||||
|
||||
case PropertyValue::Type::String:
|
||||
new (&string_v) std::shared_ptr<std::string>(other.string_v);
|
||||
return;
|
||||
|
||||
case Type::Int:
|
||||
this->int_v = other.int_v;
|
||||
return;
|
||||
|
||||
case Type::Double:
|
||||
this->double_v = other.double_v;
|
||||
return;
|
||||
|
||||
case PropertyValue::Type::List:
|
||||
new (&list_v) std::shared_ptr<std::vector<PropertyValue>>(other.list_v);
|
||||
return;
|
||||
}
|
||||
|
||||
permanent_fail("Unsupported PropertyValue::Type");
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const PropertyValue::Type type) {
|
||||
switch (type) {
|
||||
case PropertyValue::Type::Null:
|
||||
return os << "null";
|
||||
case PropertyValue::Type::Bool:
|
||||
return os << "bool";
|
||||
case PropertyValue::Type::String:
|
||||
return os << "string";
|
||||
case PropertyValue::Type::Int:
|
||||
return os << "int";
|
||||
case PropertyValue::Type::Double:
|
||||
return os << "double";
|
||||
case PropertyValue::Type::List:
|
||||
return os << "list";
|
||||
}
|
||||
permanent_fail("Unsupported PropertyValue::Type");
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const PropertyValue& value) {
|
||||
switch (value.type_) {
|
||||
case PropertyValue::Type::Null:
|
||||
return os << "Null";
|
||||
case PropertyValue::Type::Bool:
|
||||
return os << (value.Value<bool>() ? "true" : "false");
|
||||
case PropertyValue::Type::String:
|
||||
return os << value.Value<std::string>();
|
||||
case PropertyValue::Type::Int:
|
||||
return os << value.Value<int>();
|
||||
case PropertyValue::Type::Double:
|
||||
return os << value.Value<double>();
|
||||
case PropertyValue::Type::List:
|
||||
os << "[";
|
||||
for (const auto& x : value.Value<std::vector<PropertyValue>>()) {
|
||||
os << x << ",";
|
||||
}
|
||||
return os << "]";
|
||||
}
|
||||
permanent_fail("Unsupported PropertyValue::Type");
|
||||
}
|
||||
|
||||
PropertyValue& PropertyValue::operator=(const PropertyValue& other) {
|
||||
this->~PropertyValue();
|
||||
type_ = other.type_;
|
||||
|
||||
if (this != &other) {
|
||||
switch (other.type_) {
|
||||
case PropertyValue::Type::Null:
|
||||
case PropertyValue::Type::Bool:
|
||||
this->bool_v = other.bool_v;
|
||||
return *this;
|
||||
case PropertyValue::Type::String:
|
||||
new (&string_v) std::shared_ptr<std::string>(other.string_v);
|
||||
return *this;
|
||||
case PropertyValue::Type::Int:
|
||||
this->int_v = other.int_v;
|
||||
return *this;
|
||||
case PropertyValue::Type::Double:
|
||||
this->double_v = other.double_v;
|
||||
return *this;
|
||||
case PropertyValue::Type::List:
|
||||
new (&list_v) std::shared_ptr<std::vector<PropertyValue>>(other.list_v);
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
permanent_fail("Unsupported PropertyValue::Type");
|
||||
}
|
||||
|
||||
const PropertyValue PropertyValue::Null = PropertyValue();
|
||||
|
||||
PropertyValue::~PropertyValue() {
|
||||
switch (type_) {
|
||||
// destructor for primitive types does nothing
|
||||
case Type::Null:
|
||||
case Type::Bool:
|
||||
case Type::Int:
|
||||
case Type::Double:
|
||||
return;
|
||||
|
||||
// destructor for shared pointer must release
|
||||
case Type::String:
|
||||
string_v.~shared_ptr<std::string>();
|
||||
return;
|
||||
case Type::List:
|
||||
list_v.~shared_ptr<std::vector<PropertyValue>>();
|
||||
return;
|
||||
}
|
||||
permanent_fail("Unsupported PropertyValue::Type");
|
||||
}
|
99
src/storage/property_value.hpp
Normal file
99
src/storage/property_value.hpp
Normal file
@ -0,0 +1,99 @@
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "utils/exceptions/stacktrace_exception.hpp"
|
||||
#include "utils/total_ordering.hpp"
|
||||
#include "utils/underlying_cast.hpp"
|
||||
|
||||
/**
|
||||
* Encapsulation of a value and it's type encapsulated in a class that has no
|
||||
* compiled-time info about that type.
|
||||
*
|
||||
* Values can be of a number of predefined types that are enumerated in
|
||||
* PropertyValue::Type. Each such type corresponds to exactly one C++ type.
|
||||
*/
|
||||
class PropertyValue {
|
||||
private:
|
||||
/** Private default constructor, makes Null */
|
||||
PropertyValue() : type_(Type::Null) {}
|
||||
|
||||
public:
|
||||
/** A value type. Each type corresponds to exactly one C++ type */
|
||||
enum class Type : unsigned { Null, String, Bool, Int, Double, List };
|
||||
|
||||
// single static reference to Null, used whenever Null should be returned
|
||||
static const PropertyValue Null;
|
||||
|
||||
// constructors for primitive types
|
||||
PropertyValue(bool value) : type_(Type::Bool) { bool_v = value; }
|
||||
PropertyValue(int value) : type_(Type::Int) { int_v = value; }
|
||||
PropertyValue(double value) : type_(Type::Double) { double_v = value; }
|
||||
|
||||
/// constructors for non-primitive types (shared pointers)
|
||||
PropertyValue(const std::string& value) : type_(Type::String) {
|
||||
new (&string_v) std::shared_ptr<std::string>(new std::string(value));
|
||||
}
|
||||
PropertyValue(const char* value) : type_(Type::String) {
|
||||
new (&string_v) std::shared_ptr<std::string>(new std::string(value));
|
||||
}
|
||||
PropertyValue(const std::vector<PropertyValue>& value) : type_(Type::List) {
|
||||
new (&list_v) std::shared_ptr<std::vector<PropertyValue>>(
|
||||
new std::vector<PropertyValue>(value));
|
||||
}
|
||||
|
||||
// assignment op
|
||||
PropertyValue& operator=(const PropertyValue& other);
|
||||
|
||||
PropertyValue(const PropertyValue& other);
|
||||
~PropertyValue();
|
||||
|
||||
Type type() const { return type_; }
|
||||
/**
|
||||
* Returns the value of the property as given type T.
|
||||
* The behavior of this function is undefined if
|
||||
* T does not correspond to this property's type_.
|
||||
*
|
||||
* @tparam T Type to interpret the value as.
|
||||
* @return The value as type T.
|
||||
*/
|
||||
template <typename T>
|
||||
T Value() const;
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& stream,
|
||||
const PropertyValue& prop);
|
||||
|
||||
private:
|
||||
// storage for the value of the property
|
||||
union {
|
||||
bool bool_v;
|
||||
int int_v;
|
||||
double double_v;
|
||||
std::shared_ptr<std::string> string_v;
|
||||
// We support lists of values of different types, neo4j supports lists of
|
||||
// values of the same type.
|
||||
std::shared_ptr<std::vector<PropertyValue>> list_v;
|
||||
};
|
||||
|
||||
/**
|
||||
* The Type of property.
|
||||
*/
|
||||
Type type_;
|
||||
};
|
||||
|
||||
/**
|
||||
* An exception raised by the PropertyValue system. Typically when
|
||||
* trying to perform operations (such as addition) on PropertyValues
|
||||
* of incompatible Types.
|
||||
*/
|
||||
class PropertyValueException : public StacktraceException {
|
||||
public:
|
||||
using ::StacktraceException::StacktraceException;
|
||||
};
|
||||
|
||||
// stream output
|
||||
std::ostream& operator<<(std::ostream& os, const PropertyValue::Type type);
|
@ -4,7 +4,7 @@
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "typed_value.hpp"
|
||||
#include "property_value.hpp"
|
||||
|
||||
/**
|
||||
* A collection of properties accessed in a map-like way
|
||||
@ -15,33 +15,33 @@
|
||||
* @tparam TKey The type of key used in this value store.
|
||||
*/
|
||||
template <typename TKey = uint32_t>
|
||||
class TypedValueStore {
|
||||
class PropertyValueStore {
|
||||
public:
|
||||
using sptr = std::shared_ptr<TypedValueStore>;
|
||||
using sptr = std::shared_ptr<PropertyValueStore>;
|
||||
|
||||
/**
|
||||
* Returns a TypedValue (by reference) at the given key.
|
||||
* Returns a PropertyValue (by reference) at the given key.
|
||||
* If the key does not exist, the Null property is returned.
|
||||
*
|
||||
* This is NOT thread-safe, the reference might not be valid
|
||||
* when used in a multithreaded scenario.
|
||||
*
|
||||
* @param key The key for which a TypedValue is sought.
|
||||
* @param key The key for which a PropertyValue is sought.
|
||||
* @return See above.
|
||||
*/
|
||||
const TypedValue &at(const TKey &key) const {
|
||||
const PropertyValue &at(const TKey &key) const {
|
||||
for (const auto &kv : props_)
|
||||
if (kv.first == key) return kv.second;
|
||||
|
||||
return TypedValue::Null;
|
||||
return PropertyValue::Null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value for the given key. A new TypedValue instance
|
||||
* Sets the value for the given key. A new PropertyValue instance
|
||||
* is created for the given value (which is of raw type).
|
||||
*
|
||||
* @tparam TValue Type of value. It must be one of the
|
||||
* predefined possible TypedValue values (bool, string, int...)
|
||||
* predefined possible PropertyValue values (bool, string, int...)
|
||||
* @param key The key for which the property is set. The previous
|
||||
* value at the same key (if there was one) is replaced.
|
||||
* @param value The value to set.
|
||||
@ -50,7 +50,7 @@ class TypedValueStore {
|
||||
void set(const TKey &key, const TValue &value) {
|
||||
for (auto &kv : props_)
|
||||
if (kv.first == key) {
|
||||
kv.second = TypedValue(value);
|
||||
kv.second = PropertyValue(value);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -71,7 +71,7 @@ class TypedValueStore {
|
||||
void set(const TKey &key, const char *value) { set(key, std::string(value)); }
|
||||
|
||||
/**
|
||||
* Removes the TypedValue for the given key.
|
||||
* Removes the PropertyValue for the given key.
|
||||
*
|
||||
* @param key The key for which to remove the property.
|
||||
* @return The number of removed properties (0 or 1).
|
||||
@ -79,7 +79,7 @@ class TypedValueStore {
|
||||
size_t erase(const TKey &key) {
|
||||
auto found = std::find_if(
|
||||
props_.begin(), props_.end(),
|
||||
[&key](std::pair<TKey, TypedValue> &kv) { return kv.first == key; });
|
||||
[&key](std::pair<TKey, PropertyValue> &kv) { return kv.first == key; });
|
||||
|
||||
if (found != props_.end()) {
|
||||
props_.erase(found);
|
||||
@ -110,10 +110,10 @@ class TypedValueStore {
|
||||
/**
|
||||
* Accepts two functions.
|
||||
*
|
||||
* @param handler Called for each TypedValue in this collection.
|
||||
* @param handler Called for each PropertyValue in this collection.
|
||||
* @param finish Called once in the end.
|
||||
*/
|
||||
void Accept(std::function<void(const TKey, const TypedValue &)> handler,
|
||||
void Accept(std::function<void(const TKey, const PropertyValue &)> handler,
|
||||
std::function<void()> finish = {}) const {
|
||||
if (handler)
|
||||
for (const auto &prop : props_) handler(prop.first, prop.second);
|
||||
@ -122,5 +122,5 @@ class TypedValueStore {
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::pair<TKey, TypedValue>> props_;
|
||||
std::vector<std::pair<TKey, PropertyValue>> props_;
|
||||
};
|
@ -6,7 +6,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <ostream>
|
||||
#include "storage/typed_value_store.hpp"
|
||||
#include "storage/property_value_store.hpp"
|
||||
|
||||
/**
|
||||
* Writes all of the values from the given store in JSON format
|
||||
@ -15,12 +15,12 @@
|
||||
* @param store The store that should be serialized to JSON.
|
||||
* @param ostream The stream to write to.
|
||||
*/
|
||||
void TypedValuesToJson(const TypedValueStore& store,
|
||||
void PropertyValuesToJson(const PropertyValueStore& store,
|
||||
std::ostream& ostream = std::cout) {
|
||||
bool first = true;
|
||||
|
||||
auto write_key = [&ostream,
|
||||
&first](const TypedValueStore::TKey& key) -> std::ostream& {
|
||||
&first](const PropertyValueStore::TKey& key) -> std::ostream& {
|
||||
if (first) {
|
||||
ostream << '{';
|
||||
first = false;
|
||||
@ -30,21 +30,21 @@ void TypedValuesToJson(const TypedValueStore& store,
|
||||
return ostream << '"' << key << "\":";
|
||||
};
|
||||
|
||||
auto handler = [&ostream, &write_key](const TypedValueStore::TKey& key,
|
||||
const TypedValue& value) {
|
||||
auto handler = [&ostream, &write_key](const PropertyValueStore::TKey& key,
|
||||
const PropertyValue& value) {
|
||||
switch (value.type_) {
|
||||
case TypedValue::Type::Null:
|
||||
case PropertyValue::Type::Null:
|
||||
break;
|
||||
case TypedValue::Type::Bool:
|
||||
case PropertyValue::Type::Bool:
|
||||
write_key(key) << (value.Value<bool>() ? "true" : "false");
|
||||
break;
|
||||
case TypedValue::Type::String:
|
||||
case PropertyValue::Type::String:
|
||||
write_key(key) << '"' << value.Value<std::string>() << '"';
|
||||
break;
|
||||
case TypedValue::Type::Int:
|
||||
case PropertyValue::Type::Int:
|
||||
write_key(key) << value.Value<int>();
|
||||
break;
|
||||
case TypedValue::Type::Float:
|
||||
case PropertyValue::Type::Float:
|
||||
write_key(key) << value.Value<float>();
|
||||
break;
|
||||
}
|
@ -21,7 +21,7 @@ RecordAccessor<TRecord>::RecordAccessor(mvcc::VersionList<TRecord> &vlist,
|
||||
}
|
||||
|
||||
template <typename TRecord>
|
||||
const TypedValue &RecordAccessor<TRecord>::PropsAt(
|
||||
const PropertyValue &RecordAccessor<TRecord>::PropsAt(
|
||||
GraphDb::Property key) const {
|
||||
return view().properties_.at(key);
|
||||
}
|
||||
@ -32,14 +32,14 @@ size_t RecordAccessor<TRecord>::PropsErase(GraphDb::Property key) {
|
||||
}
|
||||
|
||||
template <typename TRecord>
|
||||
const TypedValueStore<GraphDb::Property> &RecordAccessor<TRecord>::Properties()
|
||||
const PropertyValueStore<GraphDb::Property> &RecordAccessor<TRecord>::Properties()
|
||||
const {
|
||||
return view().properties_;
|
||||
}
|
||||
|
||||
template <typename TRecord>
|
||||
void RecordAccessor<TRecord>::PropertiesAccept(
|
||||
std::function<void(const GraphDb::Property key, const TypedValue &prop)>
|
||||
std::function<void(const GraphDb::Property key, const PropertyValue &prop)>
|
||||
handler,
|
||||
std::function<void()> finish) const {
|
||||
view().properties_.Accept(handler, finish);
|
||||
|
@ -3,10 +3,10 @@
|
||||
#include "database/graph_db.hpp"
|
||||
#include "database/graph_db_accessor.hpp"
|
||||
#include "mvcc/version_list.hpp"
|
||||
#include "storage/typed_value.hpp"
|
||||
#include "storage/property_value.hpp"
|
||||
#include "utils/pass_key.hpp"
|
||||
|
||||
#include "storage/typed_value_store.hpp"
|
||||
#include "storage/property_value_store.hpp"
|
||||
|
||||
/**
|
||||
* An accessor to a database record (an Edge or a Vertex).
|
||||
@ -54,7 +54,7 @@ class RecordAccessor {
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
const TypedValue& PropsAt(GraphDb::Property key) const;
|
||||
const PropertyValue& PropsAt(GraphDb::Property key) const;
|
||||
|
||||
/**
|
||||
* Sets a value on the record for the given property.
|
||||
@ -80,10 +80,10 @@ class RecordAccessor {
|
||||
* Returns the properties of this record.
|
||||
* @return
|
||||
*/
|
||||
const TypedValueStore<GraphDb::Property>& Properties() const;
|
||||
const PropertyValueStore<GraphDb::Property>& Properties() const;
|
||||
|
||||
void PropertiesAccept(
|
||||
std::function<void(const GraphDb::Property key, const TypedValue& prop)>
|
||||
std::function<void(const GraphDb::Property key, const PropertyValue& prop)>
|
||||
handler,
|
||||
std::function<void()> finish = {}) const;
|
||||
|
||||
|
@ -1,382 +0,0 @@
|
||||
#include <fmt/format.h>
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
#include "storage/typed_value.hpp"
|
||||
#include "utils/assert.hpp"
|
||||
|
||||
// Value extraction template instantiations
|
||||
template <>
|
||||
bool TypedValue::Value<bool>() const {
|
||||
debug_assert(type_ == TypedValue::Type::Bool,
|
||||
"Incompatible template param and type");
|
||||
return bool_v;
|
||||
}
|
||||
|
||||
template <>
|
||||
std::string TypedValue::Value<std::string>() const {
|
||||
debug_assert(type_ == TypedValue::Type::String,
|
||||
"Incompatible template param and type");
|
||||
return *string_v;
|
||||
}
|
||||
|
||||
template <>
|
||||
int TypedValue::Value<int>() const {
|
||||
debug_assert(type_ == TypedValue::Type::Int,
|
||||
"Incompatible template param and type");
|
||||
return int_v;
|
||||
}
|
||||
|
||||
template <>
|
||||
float TypedValue::Value<float>() const {
|
||||
debug_assert(type_ == TypedValue::Type::Float,
|
||||
"Incompatible template param and type");
|
||||
return float_v;
|
||||
}
|
||||
|
||||
TypedValue::TypedValue(const TypedValue& other) : type_(other.type_) {
|
||||
switch (other.type_) {
|
||||
case TypedValue::Type::Null:
|
||||
return;
|
||||
|
||||
case TypedValue::Type::Bool:
|
||||
this->bool_v = other.bool_v;
|
||||
return;
|
||||
|
||||
case TypedValue::Type::String:
|
||||
new (&string_v) std::shared_ptr<std::string>(other.string_v);
|
||||
return;
|
||||
|
||||
case Type::Int:
|
||||
this->int_v = other.int_v;
|
||||
return;
|
||||
|
||||
case Type::Float:
|
||||
this->float_v = other.float_v;
|
||||
return;
|
||||
}
|
||||
|
||||
permanent_fail("Unsupported TypedValue::Type");
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const TypedValue::Type type) {
|
||||
switch (type) {
|
||||
case TypedValue::Type::Null:
|
||||
return os << "null";
|
||||
case TypedValue::Type::Bool:
|
||||
return os << "bool";
|
||||
case TypedValue::Type::String:
|
||||
return os << "string";
|
||||
case TypedValue::Type::Int:
|
||||
return os << "int";
|
||||
case TypedValue::Type::Float:
|
||||
return os << "float";
|
||||
}
|
||||
permanent_fail("Unsupported TypedValue::Type");
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const TypedValue& value) {
|
||||
switch (value.type_) {
|
||||
case TypedValue::Type::Null:
|
||||
return os << "Null";
|
||||
case TypedValue::Type::Bool:
|
||||
return os << (value.Value<bool>() ? "true" : "false");
|
||||
case TypedValue::Type::String:
|
||||
return os << value.Value<std::string>();
|
||||
case TypedValue::Type::Int:
|
||||
return os << value.Value<int>();
|
||||
case TypedValue::Type::Float:
|
||||
return os << value.Value<float>();
|
||||
}
|
||||
permanent_fail("Unsupported TypedValue::Type");
|
||||
}
|
||||
|
||||
TypedValue& TypedValue::operator=(TypedValue&& other) {
|
||||
// set the type of this
|
||||
const_cast<Type&>(type_) = other.type_;
|
||||
|
||||
if (this != &other) {
|
||||
switch (other.type_) {
|
||||
case TypedValue::Type::Null:
|
||||
case TypedValue::Type::Bool:
|
||||
this->bool_v = other.bool_v;
|
||||
return *this;
|
||||
case TypedValue::Type::String:
|
||||
this->string_v = std::move(other.string_v);
|
||||
return *this;
|
||||
case TypedValue::Type::Int:
|
||||
this->int_v = other.int_v;
|
||||
return *this;
|
||||
case TypedValue::Type::Float:
|
||||
this->float_v = other.float_v;
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
permanent_fail("Unsupported TypedValue::Type");
|
||||
}
|
||||
|
||||
const TypedValue TypedValue::Null = TypedValue();
|
||||
|
||||
TypedValue::~TypedValue() {
|
||||
switch (type_) {
|
||||
// destructor for primitive types does nothing
|
||||
case Type::Null:
|
||||
case Type::Bool:
|
||||
case Type::Int:
|
||||
case Type::Float:
|
||||
return;
|
||||
|
||||
// destructor for shared pointer must release
|
||||
case Type::String:
|
||||
string_v.~shared_ptr<std::string>();
|
||||
return;
|
||||
}
|
||||
permanent_fail("Unsupported TypedValue::Type");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the float value of a value.
|
||||
* The value MUST be either Float or Int.
|
||||
*
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
float ToFloat(const TypedValue& value) {
|
||||
switch (value.type_) {
|
||||
case TypedValue::Type::Int:
|
||||
return (float)value.Value<int>();
|
||||
case TypedValue::Type::Float:
|
||||
return value.Value<float>();
|
||||
|
||||
default:
|
||||
permanent_fail("Unsupported TypedValue::Type");
|
||||
}
|
||||
}
|
||||
|
||||
TypedValue operator<(const TypedValue& a, const TypedValue& b) {
|
||||
if (a.type_ == TypedValue::Type::Bool || b.type_ == TypedValue::Type::Bool)
|
||||
throw TypedValueException("Invalid 'less' operand types({} + {})", a.type_,
|
||||
b.type_);
|
||||
|
||||
if (a.type_ == TypedValue::Type::Null || b.type_ == TypedValue::Type::Null)
|
||||
return TypedValue::Null;
|
||||
|
||||
if (a.type_ == TypedValue::Type::String ||
|
||||
b.type_ == TypedValue::Type::String) {
|
||||
if (a.type_ != b.type_)
|
||||
throw TypedValueException("Invalid equality operand types({} + {})",
|
||||
a.type_, b.type_);
|
||||
else
|
||||
return a.Value<std::string>() < b.Value<std::string>();
|
||||
}
|
||||
|
||||
// at this point we only have int and float
|
||||
if (a.type_ == TypedValue::Type::Float || b.type_ == TypedValue::Type::Float)
|
||||
return ToFloat(a) < ToFloat(b);
|
||||
else
|
||||
return a.Value<int>() < b.Value<int>();
|
||||
}
|
||||
|
||||
TypedValue operator==(const TypedValue& a, const TypedValue& b) {
|
||||
if (a.type_ == TypedValue::Type::Null || b.type_ == TypedValue::Type::Null)
|
||||
return TypedValue::Null;
|
||||
|
||||
if (a.type_ == TypedValue::Type::String ||
|
||||
b.type_ == TypedValue::Type::String) {
|
||||
if (a.type_ != b.type_)
|
||||
throw TypedValueException("Invalid equality operand types({} + {})",
|
||||
a.type_, b.type_);
|
||||
else
|
||||
return a.Value<std::string>() == b.Value<std::string>();
|
||||
}
|
||||
|
||||
if (a.type_ == TypedValue::Type::Bool || b.type_ == TypedValue::Type::Bool) {
|
||||
if (a.type_ != b.type_)
|
||||
throw TypedValueException("Invalid equality operand types({} + {})",
|
||||
a.type_, b.type_);
|
||||
else
|
||||
return a.Value<bool>() == b.Value<bool>();
|
||||
}
|
||||
// at this point we only have int and float
|
||||
if (a.type_ == TypedValue::Type::Float ||
|
||||
b.type_ == TypedValue::Type::Float) {
|
||||
return ToFloat(a) == ToFloat(b);
|
||||
} else
|
||||
return a.Value<int>() == b.Value<int>();
|
||||
}
|
||||
|
||||
TypedValue operator!(const TypedValue& a) {
|
||||
switch (a.type_) {
|
||||
case TypedValue::Type::Null:
|
||||
return TypedValue::Null;
|
||||
case TypedValue::Type::Bool:
|
||||
return TypedValue(!a.Value<bool>());
|
||||
|
||||
default:
|
||||
throw TypedValueException("Invalid logical not operand type (!{})",
|
||||
a.type_);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns a numeric or string value into a string.
|
||||
*
|
||||
* @param value a value.
|
||||
* @return A string.
|
||||
*/
|
||||
std::string ValueToString(const TypedValue& value) {
|
||||
switch (value.type_) {
|
||||
case TypedValue::Type::String:
|
||||
return value.Value<std::string>();
|
||||
case TypedValue::Type::Int:
|
||||
return std::to_string(value.Value<int>());
|
||||
case TypedValue::Type::Float:
|
||||
return fmt::format("{}", value.Value<float>());
|
||||
|
||||
// unsupported situations
|
||||
default:
|
||||
permanent_fail("Unsupported TypedValue::Type");
|
||||
}
|
||||
}
|
||||
|
||||
TypedValue operator-(const TypedValue& a) {
|
||||
switch (a.type_) {
|
||||
case TypedValue::Type::Null:
|
||||
return TypedValue::Null;
|
||||
case TypedValue::Type::Int:
|
||||
return -a.Value<int>();
|
||||
case TypedValue::Type::Float:
|
||||
return -a.Value<float>();
|
||||
|
||||
default:
|
||||
throw TypedValueException("Invalid unary minus operand type (-{})",
|
||||
a.type_);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Raises a TypedValueException if the given values do not support arithmetic
|
||||
* operations. If they do, nothing happens.
|
||||
*
|
||||
* @param a First value.
|
||||
* @param b Second value.
|
||||
* @param string_ok If or not for the given operation it's valid to work with
|
||||
* String values (typically it's OK only for sum).
|
||||
* @param op_name Name of the operation, used only for exception description,
|
||||
* if raised.
|
||||
*/
|
||||
inline void EnsureArithmeticallyOk(const TypedValue& a, const TypedValue& b,
|
||||
bool string_ok, const std::string& op_name) {
|
||||
if (a.type_ == TypedValue::Type::Bool || b.type_ == TypedValue::Type::Bool)
|
||||
throw TypedValueException("Invalid {} operand types {}, {}", op_name,
|
||||
a.type_, b.type_);
|
||||
|
||||
if (string_ok) return;
|
||||
|
||||
if (a.type_ == TypedValue::Type::String ||
|
||||
b.type_ == TypedValue::Type::String)
|
||||
throw TypedValueException("Invalid subtraction operands types {}, {}",
|
||||
a.type_, b.type_);
|
||||
}
|
||||
|
||||
TypedValue operator+(const TypedValue& a, const TypedValue& b) {
|
||||
EnsureArithmeticallyOk(a, b, true, "addition");
|
||||
if (a.type_ == TypedValue::Type::Null || b.type_ == TypedValue::Type::Null)
|
||||
return TypedValue::Null;
|
||||
|
||||
// no more Bool nor Null, summing works on anything from here onward
|
||||
|
||||
if (a.type_ == TypedValue::Type::String ||
|
||||
b.type_ == TypedValue::Type::String)
|
||||
return ValueToString(a) + ValueToString(b);
|
||||
|
||||
// at this point we only have int and float
|
||||
if (a.type_ == TypedValue::Type::Float ||
|
||||
b.type_ == TypedValue::Type::Float) {
|
||||
return ToFloat(a) + ToFloat(b);
|
||||
} else
|
||||
return a.Value<int>() + b.Value<int>();
|
||||
}
|
||||
|
||||
TypedValue operator-(const TypedValue& a, const TypedValue& b) {
|
||||
EnsureArithmeticallyOk(a, b, false, "subtraction");
|
||||
|
||||
if (a.type_ == TypedValue::Type::Null || b.type_ == TypedValue::Type::Null)
|
||||
return TypedValue::Null;
|
||||
|
||||
// at this point we only have int and float
|
||||
if (a.type_ == TypedValue::Type::Float ||
|
||||
b.type_ == TypedValue::Type::Float) {
|
||||
return ToFloat(a) - ToFloat(b);
|
||||
} else
|
||||
return a.Value<int>() - b.Value<int>();
|
||||
}
|
||||
|
||||
TypedValue operator/(const TypedValue& a, const TypedValue& b) {
|
||||
EnsureArithmeticallyOk(a, b, false, "division");
|
||||
|
||||
if (a.type_ == TypedValue::Type::Null || b.type_ == TypedValue::Type::Null)
|
||||
return TypedValue::Null;
|
||||
|
||||
// at this point we only have int and float
|
||||
if (a.type_ == TypedValue::Type::Float ||
|
||||
b.type_ == TypedValue::Type::Float) {
|
||||
return ToFloat(a) / ToFloat(b);
|
||||
} else
|
||||
return a.Value<int>() / b.Value<int>();
|
||||
}
|
||||
|
||||
TypedValue operator*(const TypedValue& a, const TypedValue& b) {
|
||||
EnsureArithmeticallyOk(a, b, false, "multiplication");
|
||||
|
||||
if (a.type_ == TypedValue::Type::Null || b.type_ == TypedValue::Type::Null)
|
||||
return TypedValue::Null;
|
||||
|
||||
// at this point we only have int and float
|
||||
if (a.type_ == TypedValue::Type::Float ||
|
||||
b.type_ == TypedValue::Type::Float) {
|
||||
return ToFloat(a) * ToFloat(b);
|
||||
} else
|
||||
return a.Value<int>() * b.Value<int>();
|
||||
}
|
||||
|
||||
TypedValue operator%(const TypedValue& a, const TypedValue& b) {
|
||||
EnsureArithmeticallyOk(a, b, false, "modulo");
|
||||
|
||||
if (a.type_ == TypedValue::Type::Null || b.type_ == TypedValue::Type::Null)
|
||||
return TypedValue::Null;
|
||||
|
||||
// at this point we only have int and float
|
||||
if (a.type_ == TypedValue::Type::Float ||
|
||||
b.type_ == TypedValue::Type::Float) {
|
||||
return (float)fmod(ToFloat(a), ToFloat(b));
|
||||
} else
|
||||
return a.Value<int>() % b.Value<int>();
|
||||
}
|
||||
|
||||
inline bool IsLogicallyOk(const TypedValue& a) {
|
||||
return a.type_ == TypedValue::Type::Bool || a.type_ == TypedValue::Type::Null;
|
||||
}
|
||||
|
||||
TypedValue operator&&(const TypedValue& a, const TypedValue& b) {
|
||||
if (IsLogicallyOk(a) && IsLogicallyOk(b)) {
|
||||
if (a.type_ == TypedValue::Type::Null || b.type_ == TypedValue::Type::Null)
|
||||
return TypedValue::Null;
|
||||
else
|
||||
return a.Value<bool>() && b.Value<bool>();
|
||||
} else
|
||||
throw TypedValueException("Invalid logical and operand types({} && {})",
|
||||
a.type_, b.type_);
|
||||
}
|
||||
|
||||
TypedValue operator||(const TypedValue& a, const TypedValue& b) {
|
||||
if (IsLogicallyOk(a) && IsLogicallyOk(b)) {
|
||||
if (a.type_ == TypedValue::Type::Null || b.type_ == TypedValue::Type::Null)
|
||||
return TypedValue::Null;
|
||||
else
|
||||
return a.Value<bool>() || b.Value<bool>();
|
||||
} else
|
||||
throw TypedValueException("Invalid logical and operand types({} && {})",
|
||||
a.type_, b.type_);
|
||||
}
|
@ -5,7 +5,7 @@
|
||||
#include "database/graph_db.hpp"
|
||||
#include "mvcc/record.hpp"
|
||||
#include "mvcc/version_list.hpp"
|
||||
#include "storage/typed_value_store.hpp"
|
||||
#include "storage/property_value_store.hpp"
|
||||
|
||||
// forward declare Edge because there is a circular usage Edge <-> Vertex
|
||||
class Edge;
|
||||
@ -15,5 +15,5 @@ class Vertex : public mvcc::Record<Vertex> {
|
||||
std::vector<mvcc::VersionList<Edge>*> out_;
|
||||
std::vector<mvcc::VersionList<Edge>*> in_;
|
||||
std::vector<GraphDb::Label> labels_;
|
||||
TypedValueStore<GraphDb::Property> properties_;
|
||||
PropertyValueStore<GraphDb::Property> properties_;
|
||||
};
|
||||
|
@ -20,7 +20,7 @@ using std::endl;
|
||||
|
||||
class CPUPlan : public PlanInterface<Stream> {
|
||||
public:
|
||||
bool run(GraphDbAccessor &db_accessor, const TypedValueStore<> &args,
|
||||
bool run(GraphDbAccessor &db_accessor, const PropertyValueStore<> &args,
|
||||
Stream &stream) {
|
||||
return run_general_query(db_accessor, args, stream, CliqueQuery::FIND_ALL);
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "query/backend/cpp/typed_value.hpp"
|
||||
#include "query/plan_interface.hpp"
|
||||
#include "storage/edge_accessor.hpp"
|
||||
#include "storage/vertex_accessor.hpp"
|
||||
@ -115,7 +116,7 @@ class Bitset {
|
||||
enum CliqueQuery { SCORE_AND_LIMIT, FIND_ALL };
|
||||
|
||||
bool run_general_query(GraphDbAccessor &db_accessor,
|
||||
const TypedValueStore<> &args, Stream &stream,
|
||||
const PropertyValueStore<> &args, Stream &stream,
|
||||
enum CliqueQuery query_type) {
|
||||
if (query_type == CliqueQuery::FIND_ALL)
|
||||
stream.write_fields(
|
||||
@ -137,11 +138,11 @@ bool run_general_query(GraphDbAccessor &db_accessor,
|
||||
vertices[i].has_label(db_accessor.label("profile"))) {
|
||||
auto has_prop =
|
||||
vertices[i].PropsAt(db_accessor.property("profile_id")) == args.at(0);
|
||||
if (has_prop.type_ == TypedValue::Type::Null) continue;
|
||||
if (has_prop.type() == TypedValue::Type::Null) continue;
|
||||
if (has_prop.Value<bool>() == false) continue;
|
||||
has_prop =
|
||||
vertices[i].PropsAt(db_accessor.property("partner_id")) == args.at(1);
|
||||
if (has_prop.type_ == TypedValue::Type::Null) continue;
|
||||
if (has_prop.type() == TypedValue::Type::Null) continue;
|
||||
if (has_prop.Value<bool>() == false) continue;
|
||||
profile_index = i;
|
||||
}
|
||||
@ -152,8 +153,8 @@ bool run_general_query(GraphDbAccessor &db_accessor,
|
||||
edges_indexed.push_back(&edges[i]);
|
||||
}
|
||||
const int n = vertices_indexed.size();
|
||||
auto cmp_vertex = [](const VertexAccessor *a,
|
||||
const VertexAccessor *b) -> bool { return *a < *b; };
|
||||
auto cmp_vertex = [](const VertexAccessor *a, const VertexAccessor *b)
|
||||
-> bool { return *a < *b; };
|
||||
auto cmp_edge = [](const EdgeAccessor *a, const EdgeAccessor *b) -> bool {
|
||||
if (a->from() != b->from()) return a->from() < b->from();
|
||||
return a->to() < b->to();
|
||||
@ -163,15 +164,15 @@ bool run_general_query(GraphDbAccessor &db_accessor,
|
||||
* @param v VertexAccessor to a vertex.
|
||||
* @return position of vertex or -1 if it doesn't exist.
|
||||
*/
|
||||
auto query = [&vertices_indexed,
|
||||
&cmp_vertex](const VertexAccessor &v) -> int {
|
||||
int pos = lower_bound(vertices_indexed.begin(), vertices_indexed.end(), &v,
|
||||
cmp_vertex) -
|
||||
vertices_indexed.begin();
|
||||
if (pos == (int)vertices_indexed.size() || *vertices_indexed[pos] != v)
|
||||
return -1;
|
||||
return pos;
|
||||
};
|
||||
auto query =
|
||||
[&vertices_indexed, &cmp_vertex](const VertexAccessor &v) -> int {
|
||||
int pos = lower_bound(vertices_indexed.begin(), vertices_indexed.end(),
|
||||
&v, cmp_vertex) -
|
||||
vertices_indexed.begin();
|
||||
if (pos == (int)vertices_indexed.size() || *vertices_indexed[pos] != v)
|
||||
return -1;
|
||||
return pos;
|
||||
};
|
||||
/**
|
||||
* Update bitset of neighbours. Set bit to 1 for index of every vertex
|
||||
* endpoint of edges with type default_outfit.
|
||||
@ -203,7 +204,7 @@ bool run_general_query(GraphDbAccessor &db_accessor,
|
||||
const VertexAccessor v = *vertices_indexed[i];
|
||||
auto cmp_res = v.PropsAt(db_accessor.property("garment_id")) ==
|
||||
args.at(query_type == CliqueQuery::SCORE_AND_LIMIT ? 8 : 0);
|
||||
if (cmp_res.type_ != TypedValue::Type::Bool) continue;
|
||||
if (cmp_res.type() != TypedValue::Type::Bool) continue;
|
||||
if (cmp_res.Value<bool>() != true) continue;
|
||||
auto neigh = connected[i].Ones();
|
||||
for (int j : neigh) {
|
||||
@ -229,15 +230,16 @@ bool run_general_query(GraphDbAccessor &db_accessor,
|
||||
* @return EdgeAccessor* if it exists, nullptr otherwise.
|
||||
*/
|
||||
auto get_edge = [&edges_indexed](
|
||||
const VertexAccessor &first,
|
||||
const VertexAccessor &second) -> EdgeAccessor * {
|
||||
auto cmp_edge_to_pair = [](
|
||||
const EdgeAccessor *edge,
|
||||
const pair<const VertexAccessor *, const VertexAccessor *> &e) -> bool {
|
||||
if (edge->from() != *e.first) return edge->from() < *e.first;
|
||||
if (edge->to() != *e.second) return edge->to() < *e.second;
|
||||
return false;
|
||||
};
|
||||
const VertexAccessor &first,
|
||||
const VertexAccessor &second) -> EdgeAccessor *{
|
||||
auto cmp_edge_to_pair =
|
||||
[](const EdgeAccessor *edge,
|
||||
const pair<const VertexAccessor *, const VertexAccessor *> &e)
|
||||
-> bool {
|
||||
if (edge->from() != *e.first) return edge->from() < *e.first;
|
||||
if (edge->to() != *e.second) return edge->to() < *e.second;
|
||||
return false;
|
||||
};
|
||||
auto pos = lower_bound(edges_indexed.begin(), edges_indexed.end(),
|
||||
std::make_pair(&first, &second), cmp_edge_to_pair) -
|
||||
edges_indexed.begin();
|
||||
@ -261,18 +263,19 @@ bool run_general_query(GraphDbAccessor &db_accessor,
|
||||
* @param V index of clique vertices in vertices_indexed.
|
||||
* @return score if profile_index exists, else 0.
|
||||
*/
|
||||
auto calc_score = [&db_accessor, &vertices, &profile_index, &vertices_indexed,
|
||||
&get_edge](const std::vector<int> &V) -> int {
|
||||
int res = 0;
|
||||
if (profile_index == -1) return 0;
|
||||
for (auto x : V) {
|
||||
auto edge = get_edge(vertices[profile_index], *vertices_indexed[x]);
|
||||
if (edge == nullptr) continue;
|
||||
auto prop = edge->PropsAt(db_accessor.property("score"));
|
||||
if (prop.type_ == TypedValue::Type::Int) res += prop.Value<int>();
|
||||
}
|
||||
return res;
|
||||
};
|
||||
auto calc_score =
|
||||
[&db_accessor, &vertices, &profile_index, &vertices_indexed, &get_edge](
|
||||
const std::vector<int> &V) -> int {
|
||||
int res = 0;
|
||||
if (profile_index == -1) return 0;
|
||||
for (auto x : V) {
|
||||
auto edge = get_edge(vertices[profile_index], *vertices_indexed[x]);
|
||||
if (edge == nullptr) continue;
|
||||
auto prop = TypedValue(edge->PropsAt(db_accessor.property("score")));
|
||||
if (prop.type() == TypedValue::Type::Int) res += prop.Value<int>();
|
||||
}
|
||||
return res;
|
||||
};
|
||||
if (query_type == CliqueQuery::SCORE_AND_LIMIT) {
|
||||
auto cmp_results = [&calc_score](const std::vector<int> &first,
|
||||
const std::vector<int> &second) {
|
||||
|
@ -26,7 +26,7 @@ using std::endl;
|
||||
|
||||
class CPUPlan : public PlanInterface<Stream> {
|
||||
public:
|
||||
bool run(GraphDbAccessor &db_accessor, const TypedValueStore<> &args,
|
||||
bool run(GraphDbAccessor &db_accessor, const PropertyValueStore<> &args,
|
||||
Stream &stream) {
|
||||
return run_general_query(db_accessor, args, stream,
|
||||
CliqueQuery::SCORE_AND_LIMIT);
|
||||
|
@ -14,7 +14,7 @@ using std::endl;
|
||||
|
||||
class CPUPlan : public PlanInterface<Stream> {
|
||||
public:
|
||||
bool run(GraphDbAccessor &db_accessor, const TypedValueStore<> &args,
|
||||
bool run(GraphDbAccessor &db_accessor, const PropertyValueStore<> &args,
|
||||
Stream &stream) {
|
||||
auto v = db_accessor.insert_vertex();
|
||||
v.PropsSet(db_accessor.property("profile_id"), args.at(0));
|
||||
|
@ -13,7 +13,7 @@ using std::endl;
|
||||
|
||||
class CPUPlan : public PlanInterface<Stream> {
|
||||
public:
|
||||
bool run(GraphDbAccessor &db_accessor, const TypedValueStore<> &args,
|
||||
bool run(GraphDbAccessor &db_accessor, const PropertyValueStore<> &args,
|
||||
Stream &stream) {
|
||||
auto v = db_accessor.insert_vertex();
|
||||
v.PropsSet(db_accessor.property("profile_id"), args.at(0));
|
||||
|
@ -14,7 +14,7 @@ using std::endl;
|
||||
|
||||
class CPUPlan : public PlanInterface<Stream> {
|
||||
public:
|
||||
bool run(GraphDbAccessor &db_accessor, const TypedValueStore<> &args,
|
||||
bool run(GraphDbAccessor &db_accessor, const PropertyValueStore<> &args,
|
||||
Stream &stream) {
|
||||
auto v = db_accessor.insert_vertex();
|
||||
v.PropsSet(db_accessor.property("profile_id"), args.at(0));
|
||||
|
@ -13,7 +13,7 @@ using std::endl;
|
||||
|
||||
class CPUPlan : public PlanInterface<Stream> {
|
||||
public:
|
||||
bool run(GraphDbAccessor &db_accessor, const TypedValueStore<> &args,
|
||||
bool run(GraphDbAccessor &db_accessor, const PropertyValueStore<> &args,
|
||||
Stream &stream) {
|
||||
auto v = db_accessor.insert_vertex();
|
||||
v.add_label(db_accessor.label("garment"));
|
||||
|
@ -14,7 +14,7 @@ using std::endl;
|
||||
|
||||
class CPUPlan : public PlanInterface<Stream> {
|
||||
public:
|
||||
bool run(GraphDbAccessor &db_accessor, const TypedValueStore<> &args,
|
||||
bool run(GraphDbAccessor &db_accessor, const PropertyValueStore<> &args,
|
||||
Stream &stream) {
|
||||
auto v = db_accessor.insert_vertex();
|
||||
v.add_label(db_accessor.label("garment"));
|
||||
|
@ -14,7 +14,7 @@ using std::endl;
|
||||
|
||||
class CPUPlan : public PlanInterface<Stream> {
|
||||
public:
|
||||
bool run(GraphDbAccessor &db_accessor, const TypedValueStore<> &args,
|
||||
bool run(GraphDbAccessor &db_accessor, const PropertyValueStore<> &args,
|
||||
Stream &stream) {
|
||||
auto v = db_accessor.insert_vertex();
|
||||
v.add_label(db_accessor.label("garment"));
|
||||
|
@ -11,7 +11,7 @@ using std::endl;
|
||||
|
||||
class CPUPlan : public PlanInterface<Stream> {
|
||||
public:
|
||||
bool run(GraphDbAccessor &db_accessor, const TypedValueStore<> &args,
|
||||
bool run(GraphDbAccessor &db_accessor, const PropertyValueStore<> &args,
|
||||
Stream &stream) {
|
||||
for (auto v : db_accessor.vertices()) db_accessor.detach_remove_vertex(v);
|
||||
stream.write_empty_fields();
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "query/backend/cpp/typed_value.hpp"
|
||||
#include "query/plan_interface.hpp"
|
||||
#include "storage/edge_accessor.hpp"
|
||||
#include "storage/vertex_accessor.hpp"
|
||||
@ -13,15 +14,15 @@ using std::endl;
|
||||
|
||||
class CPUPlan : public PlanInterface<Stream> {
|
||||
public:
|
||||
bool run(GraphDbAccessor &db_accessor, const TypedValueStore<> &args,
|
||||
bool run(GraphDbAccessor &db_accessor, const PropertyValueStore<> &args,
|
||||
Stream &stream) {
|
||||
stream.write_field("g");
|
||||
for (auto vertex : db_accessor.vertices()) {
|
||||
if (vertex.has_label(db_accessor.label("garment"))) {
|
||||
auto prop = vertex.PropsAt(db_accessor.property("garment_id"));
|
||||
if (prop.type_ == TypedValue::Type::Null) continue;
|
||||
TypedValue prop = vertex.PropsAt(db_accessor.property("garment_id"));
|
||||
if (prop.type() == TypedValue::Type::Null) continue;
|
||||
auto cmp = prop == args.at(0);
|
||||
if (cmp.type_ != TypedValue::Type::Bool) continue;
|
||||
if (cmp.type() != TypedValue::Type::Bool) continue;
|
||||
if (cmp.Value<bool>() != true) continue;
|
||||
stream.write_vertex_record(vertex);
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "query/backend/cpp/typed_value.hpp"
|
||||
#include "query/plan_interface.hpp"
|
||||
#include "storage/edge_accessor.hpp"
|
||||
#include "storage/vertex_accessor.hpp"
|
||||
@ -14,16 +15,16 @@ using std::endl;
|
||||
|
||||
class CPUPlan : public PlanInterface<Stream> {
|
||||
public:
|
||||
bool run(GraphDbAccessor &db_accessor, const TypedValueStore<> &args,
|
||||
bool run(GraphDbAccessor &db_accessor, const PropertyValueStore<> &args,
|
||||
Stream &stream) {
|
||||
stream.write_field("r");
|
||||
std::vector<VertexAccessor> g1_set, g2_set;
|
||||
for (auto g1 : db_accessor.vertices()) {
|
||||
if (g1.has_label(db_accessor.label("garment"))) {
|
||||
auto prop = g1.PropsAt(db_accessor.property("garment_id"));
|
||||
if (prop.type_ == TypedValue::Type::Null) continue;
|
||||
TypedValue prop = g1.PropsAt(db_accessor.property("garment_id"));
|
||||
if (prop.type() == TypedValue::Type::Null) continue;
|
||||
auto cmp = prop == args.at(0);
|
||||
if (cmp.type_ != TypedValue::Type::Bool) continue;
|
||||
if (cmp.type() != TypedValue::Type::Bool) continue;
|
||||
if (cmp.Value<bool>() != true) continue;
|
||||
g1_set.push_back(g1);
|
||||
}
|
||||
@ -31,9 +32,9 @@ class CPUPlan : public PlanInterface<Stream> {
|
||||
for (auto g2 : db_accessor.vertices()) {
|
||||
if (g2.has_label(db_accessor.label("garment"))) {
|
||||
auto prop = g2.PropsAt(db_accessor.property("garment_id"));
|
||||
if (prop.type_ == TypedValue::Type::Null) continue;
|
||||
if (prop.type() == PropertyValue::Type::Null) continue;
|
||||
auto cmp = prop == args.at(1);
|
||||
if (cmp.type_ != TypedValue::Type::Bool) continue;
|
||||
if (cmp.type() != TypedValue::Type::Bool) continue;
|
||||
if (cmp.Value<bool>() != true) continue;
|
||||
g2_set.push_back(g2);
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ using std::endl;
|
||||
|
||||
class CPUPlan : public PlanInterface<Stream> {
|
||||
public:
|
||||
bool run(GraphDbAccessor &db_accessor, const TypedValueStore<> &args,
|
||||
bool run(GraphDbAccessor &db_accessor, const PropertyValueStore<> &args,
|
||||
Stream &stream) {
|
||||
return run_general_query(db_accessor, args, stream, "AA");
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ using std::endl;
|
||||
|
||||
class CPUPlan : public PlanInterface<Stream> {
|
||||
public:
|
||||
bool run(GraphDbAccessor &db_accessor, const TypedValueStore<> &args,
|
||||
bool run(GraphDbAccessor &db_accessor, const PropertyValueStore<> &args,
|
||||
Stream &stream) {
|
||||
return run_general_query(db_accessor, args, stream, "BB");
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ using std::endl;
|
||||
|
||||
class CPUPlan : public PlanInterface<Stream> {
|
||||
public:
|
||||
bool run(GraphDbAccessor &db_accessor, const TypedValueStore<> &args,
|
||||
bool run(GraphDbAccessor &db_accessor, const PropertyValueStore<> &args,
|
||||
Stream &stream) {
|
||||
return run_general_query(db_accessor, args, stream, "CC");
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ using std::endl;
|
||||
|
||||
class CPUPlan : public PlanInterface<Stream> {
|
||||
public:
|
||||
bool run(GraphDbAccessor &db_accessor, const TypedValueStore<> &args,
|
||||
bool run(GraphDbAccessor &db_accessor, const PropertyValueStore<> &args,
|
||||
Stream &stream) {
|
||||
return run_general_query(db_accessor, args, stream, "DD");
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ using std::endl;
|
||||
|
||||
class CPUPlan : public PlanInterface<Stream> {
|
||||
public:
|
||||
bool run(GraphDbAccessor &db_accessor, const TypedValueStore<> &args,
|
||||
bool run(GraphDbAccessor &db_accessor, const PropertyValueStore<> &args,
|
||||
Stream &stream) {
|
||||
return run_general_query(db_accessor, args, stream, "EE");
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "query/backend/cpp/typed_value.hpp"
|
||||
#include "query/plan_interface.hpp"
|
||||
#include "storage/edge_accessor.hpp"
|
||||
#include "storage/vertex_accessor.hpp"
|
||||
@ -13,15 +14,15 @@ using std::endl;
|
||||
// RETURN g
|
||||
|
||||
bool run_general_query(GraphDbAccessor &db_accessor,
|
||||
const TypedValueStore<> &args, Stream &stream,
|
||||
const PropertyValueStore<> &args, Stream &stream,
|
||||
const std::string &general_label) {
|
||||
stream.write_field("g");
|
||||
for (auto vertex : db_accessor.vertices()) {
|
||||
if (vertex.has_label(db_accessor.label("garment"))) {
|
||||
auto prop = vertex.PropsAt(db_accessor.property("garment_id"));
|
||||
if (prop.type_ == TypedValue::Type::Null) continue;
|
||||
auto cmp = prop == args.at(0);
|
||||
if (cmp.type_ != TypedValue::Type::Bool) continue;
|
||||
TypedValue prop = vertex.PropsAt(db_accessor.property("garment_id"));
|
||||
if (prop.type() == TypedValue::Type::Null) continue;
|
||||
TypedValue cmp = prop == args.at(0);
|
||||
if (cmp.type() != TypedValue::Type::Bool) continue;
|
||||
if (cmp.Value<bool>() != true) continue;
|
||||
vertex.add_label(db_accessor.label(general_label));
|
||||
stream.write_vertex_record(vertex);
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "query/backend/cpp/typed_value.hpp"
|
||||
#include "query/plan_interface.hpp"
|
||||
#include "storage/edge_accessor.hpp"
|
||||
#include "storage/vertex_accessor.hpp"
|
||||
@ -13,21 +14,21 @@ using std::endl;
|
||||
|
||||
class CPUPlan : public PlanInterface<Stream> {
|
||||
public:
|
||||
bool run(GraphDbAccessor &db_accessor, const TypedValueStore<> &args,
|
||||
bool run(GraphDbAccessor &db_accessor, const PropertyValueStore<> &args,
|
||||
Stream &stream) {
|
||||
stream.write_field("p");
|
||||
for (auto vertex : db_accessor.vertices()) {
|
||||
if (vertex.has_label(db_accessor.label("profile"))) {
|
||||
auto prop = vertex.PropsAt(db_accessor.property("profile_id"));
|
||||
if (prop.type_ == TypedValue::Type::Null) continue;
|
||||
TypedValue prop = vertex.PropsAt(db_accessor.property("profile_id"));
|
||||
if (prop.type() == TypedValue::Type::Null) continue;
|
||||
auto cmp = prop == args.at(0);
|
||||
if (cmp.type_ != TypedValue::Type::Bool) continue;
|
||||
if (cmp.type() != TypedValue::Type::Bool) continue;
|
||||
if (cmp.Value<bool>() != true) continue;
|
||||
|
||||
auto prop2 = vertex.PropsAt(db_accessor.property("partner_id"));
|
||||
if (prop2.type_ == TypedValue::Type::Null) continue;
|
||||
TypedValue prop2 = vertex.PropsAt(db_accessor.property("partner_id"));
|
||||
if (prop2.type() == TypedValue::Type::Null) continue;
|
||||
auto cmp2 = prop2 == args.at(1);
|
||||
if (cmp2.type_ != TypedValue::Type::Bool) continue;
|
||||
if (cmp2.type() != TypedValue::Type::Bool) continue;
|
||||
if (cmp2.Value<bool>() != true) continue;
|
||||
stream.write_vertex_record(vertex);
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "query/backend/cpp/typed_value.hpp"
|
||||
#include "query/plan_interface.hpp"
|
||||
#include "storage/edge_accessor.hpp"
|
||||
#include "storage/vertex_accessor.hpp"
|
||||
@ -15,27 +16,27 @@ using std::endl;
|
||||
|
||||
class CPUPlan : public PlanInterface<Stream> {
|
||||
public:
|
||||
bool run(GraphDbAccessor &db_accessor, const TypedValueStore<> &args,
|
||||
bool run(GraphDbAccessor &db_accessor, const PropertyValueStore<> &args,
|
||||
Stream &stream) {
|
||||
stream.write_field("s");
|
||||
auto profile = [&db_accessor, &args](const VertexAccessor &v) -> bool {
|
||||
auto prop = v.PropsAt(db_accessor.property("profile_id"));
|
||||
if (prop.type_ == TypedValue::Type::Null) return false;
|
||||
TypedValue prop = v.PropsAt(db_accessor.property("profile_id"));
|
||||
if (prop.type() == TypedValue::Type::Null) return false;
|
||||
auto cmp = prop == args.at(0);
|
||||
if (cmp.type_ != TypedValue::Type::Bool) return false;
|
||||
if (cmp.type() != TypedValue::Type::Bool) return false;
|
||||
if (cmp.Value<bool>() != true) return false;
|
||||
|
||||
auto prop2 = v.PropsAt(db_accessor.property("partner_id"));
|
||||
if (prop2.type_ == TypedValue::Type::Null) return false;
|
||||
TypedValue prop2 = v.PropsAt(db_accessor.property("partner_id"));
|
||||
if (prop2.type() == TypedValue::Type::Null) return false;
|
||||
auto cmp2 = prop2 == args.at(1);
|
||||
if (cmp2.type_ != TypedValue::Type::Bool) return false;
|
||||
if (cmp2.type() != TypedValue::Type::Bool) return false;
|
||||
return cmp2.Value<bool>();
|
||||
};
|
||||
auto garment = [&db_accessor, &args](const VertexAccessor &v) -> bool {
|
||||
auto prop = v.PropsAt(db_accessor.property("garment_id"));
|
||||
if (prop.type_ == TypedValue::Type::Null) return false;
|
||||
TypedValue prop = v.PropsAt(db_accessor.property("garment_id"));
|
||||
if (prop.type() == TypedValue::Type::Null) return false;
|
||||
auto cmp = prop == args.at(2);
|
||||
if (cmp.type_ != TypedValue::Type::Bool) return false;
|
||||
if (cmp.type() != TypedValue::Type::Bool) return false;
|
||||
return cmp.Value<bool>();
|
||||
};
|
||||
for (auto edge : db_accessor.edges()) {
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "query/backend/cpp/typed_value.hpp"
|
||||
#include "query/plan_interface.hpp"
|
||||
#include "storage/edge_accessor.hpp"
|
||||
#include "storage/vertex_accessor.hpp"
|
||||
@ -14,32 +15,32 @@ using std::endl;
|
||||
|
||||
class CPUPlan : public PlanInterface<Stream> {
|
||||
public:
|
||||
bool run(GraphDbAccessor &db_accessor, const TypedValueStore<> &args,
|
||||
bool run(GraphDbAccessor &db_accessor, const PropertyValueStore<> &args,
|
||||
Stream &stream) {
|
||||
stream.write_field("r");
|
||||
std::vector<VertexAccessor> g1_set, g2_set;
|
||||
for (auto g1 : db_accessor.vertices()) {
|
||||
if (g1.has_label(db_accessor.label("profile"))) {
|
||||
auto prop = g1.PropsAt(db_accessor.property("profile_id"));
|
||||
if (prop.type_ == TypedValue::Type::Null) continue;
|
||||
auto prop = TypedValue(g1.PropsAt(db_accessor.property("profile_id")));
|
||||
if (prop.type() == TypedValue::Type::Null) continue;
|
||||
auto cmp = prop == args.at(0);
|
||||
if (cmp.type_ != TypedValue::Type::Bool) continue;
|
||||
if (cmp.type() != TypedValue::Type::Bool) continue;
|
||||
if (cmp.Value<bool>() != true) continue;
|
||||
|
||||
auto prop2 = g1.PropsAt(db_accessor.property("partner_id"));
|
||||
if (prop2.type_ == TypedValue::Type::Null) continue;
|
||||
auto prop2 = TypedValue(g1.PropsAt(db_accessor.property("partner_id")));
|
||||
if (prop2.type() == TypedValue::Type::Null) continue;
|
||||
auto cmp2 = prop2 == args.at(1);
|
||||
if (cmp2.type_ != TypedValue::Type::Bool) continue;
|
||||
if (cmp2.type() != TypedValue::Type::Bool) continue;
|
||||
if (cmp2.Value<bool>() != true) continue;
|
||||
g1_set.push_back(g1);
|
||||
}
|
||||
}
|
||||
for (auto g2 : db_accessor.vertices()) {
|
||||
if (g2.has_label(db_accessor.label("garment"))) {
|
||||
auto prop = g2.PropsAt(db_accessor.property("garment_id"));
|
||||
if (prop.type_ == TypedValue::Type::Null) continue;
|
||||
auto prop = TypedValue(g2.PropsAt(db_accessor.property("garment_id")));
|
||||
if (prop.type() == TypedValue::Type::Null) continue;
|
||||
auto cmp = prop == args.at(2);
|
||||
if (cmp.type_ != TypedValue::Type::Bool) continue;
|
||||
if (cmp.type() != TypedValue::Type::Bool) continue;
|
||||
if (cmp.Value<bool>() != true) continue;
|
||||
g2_set.push_back(g2);
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "query/backend/cpp/typed_value.hpp"
|
||||
#include "query/plan_interface.hpp"
|
||||
#include "storage/edge_accessor.hpp"
|
||||
#include "storage/vertex_accessor.hpp"
|
||||
@ -15,27 +16,27 @@ using std::endl;
|
||||
|
||||
class CPUPlan : public PlanInterface<Stream> {
|
||||
public:
|
||||
bool run(GraphDbAccessor &db_accessor, const TypedValueStore<> &args,
|
||||
bool run(GraphDbAccessor &db_accessor, const PropertyValueStore<> &args,
|
||||
Stream &stream) {
|
||||
stream.write_field("s");
|
||||
auto profile = [&db_accessor, &args](const VertexAccessor &v) -> bool {
|
||||
auto prop = v.PropsAt(db_accessor.property("profile_id"));
|
||||
if (prop.type_ == TypedValue::Type::Null) return false;
|
||||
TypedValue prop = v.PropsAt(db_accessor.property("profile_id"));
|
||||
if (prop.type() == TypedValue::Type::Null) return false;
|
||||
auto cmp = prop == args.at(0);
|
||||
if (cmp.type_ != TypedValue::Type::Bool) return false;
|
||||
if (cmp.type() != TypedValue::Type::Bool) return false;
|
||||
if (cmp.Value<bool>() != true) return false;
|
||||
|
||||
auto prop2 = v.PropsAt(db_accessor.property("partner_id"));
|
||||
if (prop2.type_ == TypedValue::Type::Null) return false;
|
||||
TypedValue prop2 = v.PropsAt(db_accessor.property("partner_id"));
|
||||
if (prop2.type() == TypedValue::Type::Null) return false;
|
||||
auto cmp2 = prop2 == args.at(1);
|
||||
if (cmp2.type_ != TypedValue::Type::Bool) return false;
|
||||
if (cmp2.type() != TypedValue::Type::Bool) return false;
|
||||
return cmp2.Value<bool>();
|
||||
};
|
||||
auto garment = [&db_accessor, &args](const VertexAccessor &v) -> bool {
|
||||
auto prop = v.PropsAt(db_accessor.property("garment_id"));
|
||||
if (prop.type_ == TypedValue::Type::Null) return false;
|
||||
TypedValue prop = v.PropsAt(db_accessor.property("garment_id"));
|
||||
if (prop.type() == TypedValue::Type::Null) return false;
|
||||
auto cmp = prop == args.at(2);
|
||||
if (cmp.type_ != TypedValue::Type::Bool) return false;
|
||||
if (cmp.type() != TypedValue::Type::Bool) return false;
|
||||
return cmp.Value<bool>();
|
||||
};
|
||||
for (auto edge : db_accessor.edges()) {
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "query/backend/cpp/typed_value.hpp"
|
||||
#include "query/plan_interface.hpp"
|
||||
#include "using.hpp"
|
||||
|
||||
@ -11,7 +12,7 @@ using std::endl;
|
||||
|
||||
class CPUPlan : public PlanInterface<Stream> {
|
||||
public:
|
||||
bool run(GraphDbAccessor &db_accessor, const TypedValueStore<> &args,
|
||||
bool run(GraphDbAccessor &db_accessor, const PropertyValueStore<> &args,
|
||||
Stream &stream) {
|
||||
db_accessor.transaction_.commit();
|
||||
return true;
|
||||
|
@ -5,11 +5,12 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "query/backend/cpp/typed_value.hpp"
|
||||
#include "storage/edge_accessor.hpp"
|
||||
#include "storage/vertex_accessor.hpp"
|
||||
|
||||
void write_properties(std::ostream &os, const GraphDbAccessor &access,
|
||||
const TypedValueStore<GraphDb::Property> &properties) {
|
||||
const PropertyValueStore<GraphDb::Property> &properties) {
|
||||
if (properties.size() > 0) {
|
||||
os << "{";
|
||||
for (auto x : properties) {
|
||||
|
130
tests/unit/property_value_store.cpp
Normal file
130
tests/unit/property_value_store.cpp
Normal file
@ -0,0 +1,130 @@
|
||||
//
|
||||
// Copyright 2017 Memgraph
|
||||
// Created by Florijan Stamenkovic on 24.01.17..
|
||||
//
|
||||
#include <vector>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "storage/property_value_store.hpp"
|
||||
#include "storage/property_value.hpp"
|
||||
|
||||
using std::string;
|
||||
|
||||
TEST(PropertyValueStore, At) {
|
||||
std::string some_string = "something";
|
||||
|
||||
PropertyValueStore<> props;
|
||||
EXPECT_EQ(PropertyValue(props.at(0)).type(), PropertyValue::Type::Null);
|
||||
props.set(0, some_string);
|
||||
EXPECT_EQ(PropertyValue(props.at(0)).Value<string>(), some_string);
|
||||
props.set(120, 42);
|
||||
EXPECT_EQ(PropertyValue(props.at(120)).Value<int>(), 42);
|
||||
}
|
||||
|
||||
TEST(PropertyValueStore, AtNull) {
|
||||
PropertyValueStore<> props;
|
||||
EXPECT_EQ(props.at(0).type(), PropertyValue::Type::Null);
|
||||
EXPECT_EQ(props.at(100).type(), PropertyValue::Type::Null);
|
||||
|
||||
// set one prop and test it's not null
|
||||
props.set(0, true);
|
||||
EXPECT_NE(props.at(0).type(), PropertyValue::Type::Null);
|
||||
EXPECT_EQ(props.at(100).type(), PropertyValue::Type::Null);
|
||||
}
|
||||
|
||||
TEST(PropertyValueStore, Remove) {
|
||||
// set some props
|
||||
PropertyValueStore<> props;
|
||||
props.set(11, "a");
|
||||
props.set(30, "b");
|
||||
EXPECT_NE(props.at(11).type(), PropertyValue::Type::Null);
|
||||
EXPECT_NE(props.at(30).type(), PropertyValue::Type::Null);
|
||||
EXPECT_EQ(props.size(), 2);
|
||||
|
||||
props.erase(11);
|
||||
EXPECT_EQ(props.size(), 1);
|
||||
EXPECT_EQ(props.at(11).type(), PropertyValue::Type::Null);
|
||||
|
||||
EXPECT_EQ(props.erase(30), 1);
|
||||
EXPECT_EQ(props.size(), 0);
|
||||
EXPECT_EQ(props.at(30).type(), PropertyValue::Type::Null);
|
||||
|
||||
EXPECT_EQ(props.erase(1000), 0);
|
||||
}
|
||||
|
||||
TEST(PropertyValueStore, Replace) {
|
||||
PropertyValueStore<> props;
|
||||
props.set(10, 42);
|
||||
EXPECT_EQ(props.at(10).Value<int>(), 42);
|
||||
props.set(10, 0.25f);
|
||||
EXPECT_EQ(props.at(10).type(), PropertyValue::Type::Double);
|
||||
EXPECT_FLOAT_EQ(props.at(10).Value<double>(), 0.25);
|
||||
}
|
||||
|
||||
TEST(PropertyValueStore, Size) {
|
||||
PropertyValueStore<> props;
|
||||
EXPECT_EQ(props.size(), 0);
|
||||
|
||||
props.set(0, "something");
|
||||
EXPECT_EQ(props.size(), 1);
|
||||
props.set(0, true);
|
||||
EXPECT_EQ(props.size(), 1);
|
||||
props.set(1, true);
|
||||
EXPECT_EQ(props.size(), 2);
|
||||
|
||||
for (int i = 0; i < 100; ++i) props.set(i + 20, true);
|
||||
EXPECT_EQ(props.size(), 102);
|
||||
|
||||
props.erase(0);
|
||||
EXPECT_EQ(props.size(), 101);
|
||||
props.erase(0);
|
||||
EXPECT_EQ(props.size(), 101);
|
||||
props.erase(1);
|
||||
EXPECT_EQ(props.size(), 100);
|
||||
}
|
||||
|
||||
TEST(PropertyValueStore, Accept) {
|
||||
int count_props = 0;
|
||||
int count_finish = 0;
|
||||
|
||||
auto handler =
|
||||
[&](const uint32_t, const PropertyValue&) { count_props += 1; };
|
||||
auto finish = [&]() { count_finish += 1; };
|
||||
|
||||
PropertyValueStore<uint32_t> props;
|
||||
props.Accept(handler, finish);
|
||||
EXPECT_EQ(count_props, 0);
|
||||
EXPECT_EQ(count_finish, 1);
|
||||
|
||||
props.Accept(handler);
|
||||
EXPECT_EQ(count_props, 0);
|
||||
EXPECT_EQ(count_finish, 1);
|
||||
|
||||
props.set(0, 20);
|
||||
props.set(1, "bla");
|
||||
|
||||
props.Accept(handler, finish);
|
||||
EXPECT_EQ(count_props, 2);
|
||||
EXPECT_EQ(count_finish, 2);
|
||||
}
|
||||
|
||||
TEST(PropertyValueStore, InsertRetrieveList) {
|
||||
PropertyValueStore<> props;
|
||||
props.set(0, std::vector<PropertyValue>{1, true, 2.5, "something",
|
||||
PropertyValue::Null});
|
||||
auto p = props.at(0);
|
||||
|
||||
EXPECT_EQ(p.type(), PropertyValue::Type::List);
|
||||
auto l = p.Value<std::vector<PropertyValue>>();
|
||||
EXPECT_EQ(l.size(), 5);
|
||||
EXPECT_EQ(l[0].type(), PropertyValue::Type::Int);
|
||||
EXPECT_EQ(l[0].Value<int>(), 1);
|
||||
EXPECT_EQ(l[1].type(), PropertyValue::Type::Bool);
|
||||
EXPECT_EQ(l[1].Value<bool>(), true);
|
||||
EXPECT_EQ(l[2].type(), PropertyValue::Type::Double);
|
||||
EXPECT_EQ(l[2].Value<double>(), 2.5);
|
||||
EXPECT_EQ(l[3].type(), PropertyValue::Type::String);
|
||||
EXPECT_EQ(l[3].Value<std::string>(), "something");
|
||||
EXPECT_EQ(l[4].type(), PropertyValue::Type::Null);
|
||||
}
|
@ -7,7 +7,7 @@
|
||||
#include "dbms/dbms.hpp"
|
||||
|
||||
#include "storage/edge_accessor.hpp"
|
||||
#include "storage/typed_value.hpp"
|
||||
#include "storage/property_value.hpp"
|
||||
#include "storage/vertex_accessor.hpp"
|
||||
|
||||
TEST(RecordAccessor, Properties) {
|
||||
@ -19,17 +19,17 @@ TEST(RecordAccessor, Properties) {
|
||||
|
||||
auto property = dba.property("PropName");
|
||||
auto property_other = dba.property("Other");
|
||||
EXPECT_EQ(vertex.PropsAt(property).type_, TypedValue::Type::Null);
|
||||
EXPECT_EQ(vertex.PropsAt(property).type(), PropertyValue::Type::Null);
|
||||
|
||||
vertex.PropsSet(property, 42);
|
||||
EXPECT_EQ(vertex.PropsAt(property).Value<int>(), 42);
|
||||
EXPECT_EQ(properties.at(property).Value<int>(), 42);
|
||||
EXPECT_EQ(vertex.PropsAt(property_other).type_, TypedValue::Type::Null);
|
||||
EXPECT_EQ(properties.at(property_other).type_, TypedValue::Type::Null);
|
||||
EXPECT_EQ(vertex.PropsAt(property_other).type(), PropertyValue::Type::Null);
|
||||
EXPECT_EQ(properties.at(property_other).type(), PropertyValue::Type::Null);
|
||||
|
||||
vertex.PropsErase(property);
|
||||
EXPECT_EQ(vertex.PropsAt(property).type_, TypedValue::Type::Null);
|
||||
EXPECT_EQ(properties.at(property).type_, TypedValue::Type::Null);
|
||||
EXPECT_EQ(vertex.PropsAt(property).type(), PropertyValue::Type::Null);
|
||||
EXPECT_EQ(properties.at(property).type(), PropertyValue::Type::Null);
|
||||
}
|
||||
|
||||
TEST(RecordAccessor, DbAccessor) {
|
||||
|
@ -20,11 +20,11 @@ TEST(RecordStreamMocker, Headers) {
|
||||
TEST(RecordStreamMocker, OneValue) {
|
||||
bolt::RecordStreamMocker rs;
|
||||
rs.write_field("n");
|
||||
rs.write(TypedValue(5));
|
||||
rs.write(PropertyValue(5));
|
||||
ASSERT_EQ(rs.count_message_columns("fields"), 1);
|
||||
std::vector<antlrcpp::Any> output = rs.get_message_column("fields", 0);
|
||||
ASSERT_EQ(output.size(), 1);
|
||||
auto val = output[0].as<TypedValue>().Value<int>();
|
||||
auto val = output[0].as<PropertyValue>().Value<int>();
|
||||
ASSERT_EQ(val, 5);
|
||||
}
|
||||
|
||||
@ -33,14 +33,14 @@ TEST(RecordStreamMocker, OneListOfInts) {
|
||||
rs.write_field("n");
|
||||
std::vector<int> expected_output = {5, 4, 6, 7};
|
||||
rs.write_list_header(expected_output.size());
|
||||
for (auto x : expected_output) rs.write(TypedValue(x));
|
||||
for (auto x : expected_output) rs.write(PropertyValue(x));
|
||||
ASSERT_EQ(rs.count_message_columns("fields"), 1);
|
||||
std::vector<antlrcpp::Any> output = rs.get_message_column("fields", 0);
|
||||
ASSERT_EQ(output.size(), 1);
|
||||
std::vector<antlrcpp::Any> list = output[0].as<std::vector<antlrcpp::Any>>();
|
||||
ASSERT_EQ(list.size(), expected_output.size());
|
||||
for (int i = 0; i < list.size(); ++i) {
|
||||
auto val = list[i].as<TypedValue>().Value<int>();
|
||||
auto val = list[i].as<PropertyValue>().Value<int>();
|
||||
ASSERT_EQ(val, expected_output[i]);
|
||||
}
|
||||
}
|
||||
@ -53,18 +53,18 @@ TEST(RecordStreamMocker, OneListOfIntAndList) {
|
||||
rs.write_list_header(2);
|
||||
rs.write(expected_value);
|
||||
rs.write_list_header(4);
|
||||
for (auto x : expected_output) rs.write(TypedValue(x));
|
||||
for (auto x : expected_output) rs.write(PropertyValue(x));
|
||||
|
||||
ASSERT_EQ(rs.count_message_columns("fields"), 1);
|
||||
std::vector<antlrcpp::Any> output = rs.get_message_column("fields", 0);
|
||||
ASSERT_EQ(output.size(), 1);
|
||||
std::vector<antlrcpp::Any> list = output[0].as<std::vector<antlrcpp::Any>>();
|
||||
ASSERT_EQ(list.size(), 2);
|
||||
ASSERT_EQ(list[0].as<TypedValue>().Value<int>(), expected_value);
|
||||
ASSERT_EQ(list[0].as<PropertyValue>().Value<int>(), expected_value);
|
||||
|
||||
auto list_inside = list[1].as<std::vector<antlrcpp::Any>>();
|
||||
for (int i = 0; i < list_inside.size(); ++i) {
|
||||
auto val = list_inside[i].as<TypedValue>().Value<int>();
|
||||
auto val = list_inside[i].as<PropertyValue>().Value<int>();
|
||||
ASSERT_EQ(val, expected_output[i]);
|
||||
}
|
||||
}
|
||||
|
@ -6,25 +6,23 @@
|
||||
#include <vector>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "query/backend/cpp/typed_value.hpp"
|
||||
|
||||
#include "storage/typed_value.hpp"
|
||||
#include "storage/typed_value_store.hpp"
|
||||
namespace {
|
||||
|
||||
TypedValueStore<> MakePropsAllTypes() {
|
||||
TypedValueStore<> props;
|
||||
props.set(0, true);
|
||||
props.set(1, "something");
|
||||
props.set(2, 42);
|
||||
props.set(3, 0.5f);
|
||||
return props;
|
||||
std::vector<TypedValue> MakePropsAllTypes() {
|
||||
return {
|
||||
true, "something", 42, 0.5, TypedValue::Null,
|
||||
std::vector<TypedValue>{true, "something", 42, 0.5, TypedValue::Null}};
|
||||
}
|
||||
}
|
||||
|
||||
void EXPECT_PROP_FALSE(const TypedValue& a) {
|
||||
EXPECT_TRUE(a.type_ == TypedValue::Type::Bool && !a.Value<bool>());
|
||||
EXPECT_TRUE(a.type() == TypedValue::Type::Bool && !a.Value<bool>());
|
||||
}
|
||||
|
||||
void EXPECT_PROP_TRUE(const TypedValue& a) {
|
||||
EXPECT_TRUE(a.type_ == TypedValue::Type::Bool && a.Value<bool>());
|
||||
EXPECT_TRUE(a.type() == TypedValue::Type::Bool && a.Value<bool>());
|
||||
}
|
||||
|
||||
void EXPECT_PROP_EQ(const TypedValue& a, const TypedValue& b) {
|
||||
@ -32,7 +30,7 @@ void EXPECT_PROP_EQ(const TypedValue& a, const TypedValue& b) {
|
||||
}
|
||||
|
||||
void EXPECT_PROP_ISNULL(const TypedValue& a) {
|
||||
EXPECT_TRUE(a.type_ == TypedValue::Type::Null);
|
||||
EXPECT_TRUE(a.type() == TypedValue::Type::Null);
|
||||
}
|
||||
|
||||
void EXPECT_PROP_NE(const TypedValue& a, const TypedValue& b) {
|
||||
@ -40,20 +38,20 @@ void EXPECT_PROP_NE(const TypedValue& a, const TypedValue& b) {
|
||||
}
|
||||
|
||||
TEST(TypedValue, CreationTypes) {
|
||||
EXPECT_TRUE(TypedValue::Null.type_ == TypedValue::Type::Null);
|
||||
EXPECT_TRUE(TypedValue::Null.type() == TypedValue::Type::Null);
|
||||
|
||||
EXPECT_TRUE(TypedValue(true).type_ == TypedValue::Type::Bool);
|
||||
EXPECT_TRUE(TypedValue(false).type_ == TypedValue::Type::Bool);
|
||||
EXPECT_TRUE(TypedValue(true).type() == TypedValue::Type::Bool);
|
||||
EXPECT_TRUE(TypedValue(false).type() == TypedValue::Type::Bool);
|
||||
|
||||
EXPECT_TRUE(TypedValue(std::string("form string class")).type_ ==
|
||||
EXPECT_TRUE(TypedValue(std::string("form string class")).type() ==
|
||||
TypedValue::Type::String);
|
||||
EXPECT_TRUE(TypedValue("form c-string").type_ == TypedValue::Type::String);
|
||||
EXPECT_TRUE(TypedValue("form c-string").type() == TypedValue::Type::String);
|
||||
|
||||
EXPECT_TRUE(TypedValue(0).type_ == TypedValue::Type::Int);
|
||||
EXPECT_TRUE(TypedValue(42).type_ == TypedValue::Type::Int);
|
||||
EXPECT_TRUE(TypedValue(0).type() == TypedValue::Type::Int);
|
||||
EXPECT_TRUE(TypedValue(42).type() == TypedValue::Type::Int);
|
||||
|
||||
EXPECT_TRUE(TypedValue(0.0f).type_ == TypedValue::Type::Float);
|
||||
EXPECT_TRUE(TypedValue(42.5f).type_ == TypedValue::Type::Float);
|
||||
EXPECT_TRUE(TypedValue(0.0).type() == TypedValue::Type::Double);
|
||||
EXPECT_TRUE(TypedValue(42.5).type() == TypedValue::Type::Double);
|
||||
}
|
||||
|
||||
TEST(TypedValue, CreationValues) {
|
||||
@ -65,7 +63,7 @@ TEST(TypedValue, CreationValues) {
|
||||
|
||||
EXPECT_EQ(TypedValue(55).Value<int>(), 55);
|
||||
|
||||
EXPECT_FLOAT_EQ(TypedValue(66.6f).Value<float>(), 66.6f);
|
||||
EXPECT_FLOAT_EQ(TypedValue(66.6).Value<double>(), 66.6);
|
||||
}
|
||||
|
||||
TEST(TypedValue, Equals) {
|
||||
@ -75,22 +73,26 @@ TEST(TypedValue, Equals) {
|
||||
EXPECT_PROP_EQ(TypedValue(42), TypedValue(42));
|
||||
EXPECT_PROP_NE(TypedValue(0), TypedValue(1));
|
||||
|
||||
EXPECT_PROP_NE(TypedValue(0.5f), TypedValue(0.12f));
|
||||
EXPECT_PROP_EQ(TypedValue(0.123f), TypedValue(0.123f));
|
||||
EXPECT_PROP_NE(TypedValue(0.5), TypedValue(0.12));
|
||||
EXPECT_PROP_EQ(TypedValue(0.123), TypedValue(0.123));
|
||||
|
||||
EXPECT_PROP_EQ(TypedValue(2), TypedValue(2.0f));
|
||||
EXPECT_PROP_NE(TypedValue(2), TypedValue(2.1f));
|
||||
EXPECT_PROP_EQ(TypedValue(2), TypedValue(2.0));
|
||||
EXPECT_PROP_NE(TypedValue(2), TypedValue(2.1));
|
||||
|
||||
EXPECT_PROP_NE(TypedValue("str1"), TypedValue("str2"));
|
||||
EXPECT_PROP_EQ(TypedValue("str3"), TypedValue("str3"));
|
||||
EXPECT_PROP_EQ(TypedValue(std::string("str3")), TypedValue("str3"));
|
||||
|
||||
EXPECT_PROP_NE(TypedValue(std::vector<TypedValue>{1}), TypedValue(1));
|
||||
EXPECT_PROP_EQ(TypedValue(std::vector<TypedValue>{1, true, "a"}),
|
||||
TypedValue(std::vector<TypedValue>{1, true, "a"}));
|
||||
}
|
||||
|
||||
TEST(TypedValue, Less) {
|
||||
// not_bool_type < bool -> exception
|
||||
TypedValueStore<> props = MakePropsAllTypes();
|
||||
for (int i = 0; i < props.size() + 1; ++i) {
|
||||
if (props.at(i).type_ == TypedValue::Type::Bool) continue;
|
||||
auto props = MakePropsAllTypes();
|
||||
for (int i = 0; i < (int)props.size(); ++i) {
|
||||
if (props.at(i).type() == TypedValue::Type::Bool) continue;
|
||||
// the comparison should raise an exception
|
||||
// cast to (void) so the compiler does not complain about unused comparison
|
||||
// result
|
||||
@ -99,8 +101,8 @@ TEST(TypedValue, Less) {
|
||||
|
||||
// not_bool_type < Null = Null
|
||||
props = MakePropsAllTypes();
|
||||
for (int i = 0; i < props.size() + 1; ++i) {
|
||||
if (props.at(i).type_ == TypedValue::Type::Bool) continue;
|
||||
for (int i = 0; i < (int)props.size(); ++i) {
|
||||
if (props.at(i).type() == TypedValue::Type::Bool) continue;
|
||||
EXPECT_PROP_ISNULL(props.at(i) < TypedValue::Null);
|
||||
}
|
||||
|
||||
@ -109,16 +111,16 @@ TEST(TypedValue, Less) {
|
||||
EXPECT_PROP_FALSE(TypedValue(2) < TypedValue(2));
|
||||
EXPECT_PROP_FALSE(TypedValue(3) < TypedValue(2));
|
||||
|
||||
// float tests
|
||||
EXPECT_PROP_TRUE(TypedValue(2.1f) < TypedValue(2.5f));
|
||||
EXPECT_PROP_FALSE(TypedValue(2.0f) < TypedValue(2.0f));
|
||||
EXPECT_PROP_FALSE(TypedValue(2.5f) < TypedValue(2.4f));
|
||||
// double tests
|
||||
EXPECT_PROP_TRUE(TypedValue(2.1) < TypedValue(2.5));
|
||||
EXPECT_PROP_FALSE(TypedValue(2.0) < TypedValue(2.0));
|
||||
EXPECT_PROP_FALSE(TypedValue(2.5) < TypedValue(2.4));
|
||||
|
||||
// implicit casting int->float
|
||||
EXPECT_PROP_TRUE(TypedValue(2) < TypedValue(2.1f));
|
||||
EXPECT_PROP_FALSE(TypedValue(2.1f) < TypedValue(2));
|
||||
EXPECT_PROP_FALSE(TypedValue(2) < TypedValue(1.5f));
|
||||
EXPECT_PROP_TRUE(TypedValue(1.5f) < TypedValue(2));
|
||||
// implicit casting int->double
|
||||
EXPECT_PROP_TRUE(TypedValue(2) < TypedValue(2.1));
|
||||
EXPECT_PROP_FALSE(TypedValue(2.1) < TypedValue(2));
|
||||
EXPECT_PROP_FALSE(TypedValue(2) < TypedValue(1.5));
|
||||
EXPECT_PROP_TRUE(TypedValue(1.5) < TypedValue(2));
|
||||
|
||||
// string tests
|
||||
EXPECT_PROP_TRUE(TypedValue("a") < TypedValue("b"));
|
||||
@ -130,15 +132,15 @@ TEST(TypedValue, LogicalNot) {
|
||||
EXPECT_PROP_EQ(!TypedValue(true), TypedValue(false));
|
||||
EXPECT_PROP_ISNULL(!TypedValue::Null);
|
||||
EXPECT_THROW(!TypedValue(0), TypedValueException);
|
||||
EXPECT_THROW(!TypedValue(0.2f), TypedValueException);
|
||||
EXPECT_THROW(!TypedValue(0.2), TypedValueException);
|
||||
EXPECT_THROW(!TypedValue("something"), TypedValueException);
|
||||
}
|
||||
|
||||
TEST(TypedValue, UnaryMinus) {
|
||||
EXPECT_TRUE((-TypedValue::Null).type_ == TypedValue::Type::Null);
|
||||
EXPECT_TRUE((-TypedValue::Null).type() == TypedValue::Type::Null);
|
||||
|
||||
EXPECT_PROP_EQ((-TypedValue(2).Value<int>()), -2);
|
||||
EXPECT_FLOAT_EQ((-TypedValue(2.0f).Value<float>()), -2.0f);
|
||||
EXPECT_FLOAT_EQ((-TypedValue(2.0).Value<double>()), -2.0);
|
||||
|
||||
EXPECT_THROW(-TypedValue(true), TypedValueException);
|
||||
EXPECT_THROW(-TypedValue("something"), TypedValueException);
|
||||
@ -155,24 +157,30 @@ TEST(TypedValue, UnaryMinus) {
|
||||
* the results.
|
||||
*/
|
||||
void ExpectArithmeticThrowsAndNull(
|
||||
bool string_ok,
|
||||
bool string_list_ok,
|
||||
std::function<TypedValue(const TypedValue&, const TypedValue&)> op) {
|
||||
// arithmetic ops that raise
|
||||
TypedValueStore<> props = MakePropsAllTypes();
|
||||
for (int i = 0; i < props.size() + 1; ++i) {
|
||||
EXPECT_THROW(op(TypedValue(true), props.at(i)), TypedValueException);
|
||||
EXPECT_THROW(op(props.at(i), TypedValue(true)), TypedValueException);
|
||||
if (!string_ok) {
|
||||
auto props = MakePropsAllTypes();
|
||||
for (int i = 0; i < (int)props.size(); ++i) {
|
||||
if (!string_list_ok || props.at(i).type() == TypedValue::Type::String) {
|
||||
EXPECT_THROW(op(TypedValue(true), props.at(i)), TypedValueException);
|
||||
EXPECT_THROW(op(props.at(i), TypedValue(true)), TypedValueException);
|
||||
}
|
||||
if (!string_list_ok) {
|
||||
EXPECT_THROW(op(TypedValue("some"), props.at(i)), TypedValueException);
|
||||
EXPECT_THROW(op(props.at(i), TypedValue("some")), TypedValueException);
|
||||
EXPECT_THROW(op(TypedValue("[1]"), props.at(i)), TypedValueException);
|
||||
EXPECT_THROW(op(props.at(i), TypedValue("[]")), TypedValueException);
|
||||
}
|
||||
}
|
||||
|
||||
// null resulting ops
|
||||
props = MakePropsAllTypes();
|
||||
for (int i = 0; i < props.size() + 1; ++i) {
|
||||
if (props.at(i).type_ == TypedValue::Type::Bool) continue;
|
||||
if (!string_ok && props.at(i).type_ == TypedValue::Type::String) continue;
|
||||
for (int i = 0; i < (int)props.size(); ++i) {
|
||||
if (props.at(i).type() == TypedValue::Type::Bool) continue;
|
||||
if (!string_list_ok && (props.at(i).type() == TypedValue::Type::String ||
|
||||
props.at(i).type() == TypedValue::Type::List))
|
||||
continue;
|
||||
|
||||
EXPECT_PROP_ISNULL(op(props.at(i), TypedValue::Null));
|
||||
EXPECT_PROP_ISNULL(op(TypedValue::Null, props.at(i)));
|
||||
@ -185,17 +193,28 @@ TEST(TypedValue, Sum) {
|
||||
|
||||
// sum of props of the same type
|
||||
EXPECT_EQ((TypedValue(2) + TypedValue(3)).Value<int>(), 5);
|
||||
EXPECT_FLOAT_EQ((TypedValue(2.5f) + TypedValue(1.25f)).Value<float>(), 3.75);
|
||||
EXPECT_FLOAT_EQ((TypedValue(2.5) + TypedValue(1.25)).Value<double>(), 3.75);
|
||||
EXPECT_EQ((TypedValue("one") + TypedValue("two")).Value<std::string>(),
|
||||
"onetwo");
|
||||
|
||||
// sum of string and numbers
|
||||
EXPECT_EQ((TypedValue("one") + TypedValue(1)).Value<std::string>(), "one1");
|
||||
EXPECT_EQ((TypedValue(1) + TypedValue("one")).Value<std::string>(), "1one");
|
||||
EXPECT_EQ((TypedValue("one") + TypedValue(3.2f)).Value<std::string>(),
|
||||
EXPECT_EQ((TypedValue("one") + TypedValue(3.2)).Value<std::string>(),
|
||||
"one3.2");
|
||||
EXPECT_EQ((TypedValue(3.2f) + TypedValue("one")).Value<std::string>(),
|
||||
EXPECT_EQ((TypedValue(3.2) + TypedValue("one")).Value<std::string>(),
|
||||
"3.2one");
|
||||
std::vector<TypedValue> in = {1, 2, true, "a"};
|
||||
std::vector<TypedValue> out1 = {2, 1, 2, true, "a"};
|
||||
std::vector<TypedValue> out2 = {1, 2, true, "a", 2};
|
||||
std::vector<TypedValue> out3 = {1, 2, true, "a", 1, 2, true, "a"};
|
||||
EXPECT_PROP_EQ(
|
||||
(TypedValue(2) + TypedValue(in)).Value<std::vector<TypedValue>>(), out1);
|
||||
std::cerr << (TypedValue(2) + TypedValue(in)) << "\n";
|
||||
EXPECT_PROP_EQ(
|
||||
(TypedValue(in) + TypedValue(2)).Value<std::vector<TypedValue>>(), out2);
|
||||
EXPECT_PROP_EQ(
|
||||
(TypedValue(in) + TypedValue(in)).Value<std::vector<TypedValue>>(), out3);
|
||||
}
|
||||
|
||||
TEST(TypedValue, Difference) {
|
||||
@ -204,11 +223,11 @@ TEST(TypedValue, Difference) {
|
||||
|
||||
// difference of props of the same type
|
||||
EXPECT_EQ((TypedValue(2) - TypedValue(3)).Value<int>(), -1);
|
||||
EXPECT_FLOAT_EQ((TypedValue(2.5f) - TypedValue(2.25f)).Value<float>(), 0.25);
|
||||
EXPECT_FLOAT_EQ((TypedValue(2.5) - TypedValue(2.25)).Value<double>(), 0.25);
|
||||
|
||||
// implicit casting
|
||||
EXPECT_FLOAT_EQ((TypedValue(2) - TypedValue(0.5f)).Value<float>(), 1.5f);
|
||||
EXPECT_FLOAT_EQ((TypedValue(2.5f) - TypedValue(2)).Value<float>(), 0.5f);
|
||||
EXPECT_FLOAT_EQ((TypedValue(2) - TypedValue(0.5)).Value<double>(), 1.5);
|
||||
EXPECT_FLOAT_EQ((TypedValue(2.5) - TypedValue(2)).Value<double>(), 0.5);
|
||||
}
|
||||
|
||||
TEST(TypedValue, Divison) {
|
||||
@ -218,11 +237,11 @@ TEST(TypedValue, Divison) {
|
||||
EXPECT_PROP_EQ(TypedValue(10) / TypedValue(2), TypedValue(5));
|
||||
EXPECT_PROP_EQ(TypedValue(10) / TypedValue(4), TypedValue(2));
|
||||
|
||||
EXPECT_PROP_EQ(TypedValue(10.0f) / TypedValue(2.0f), TypedValue(5.0f));
|
||||
EXPECT_FLOAT_EQ((TypedValue(10.0f) / TypedValue(4.0f)).Value<float>(), 2.5f);
|
||||
EXPECT_PROP_EQ(TypedValue(10.0) / TypedValue(2.0), TypedValue(5.0));
|
||||
EXPECT_FLOAT_EQ((TypedValue(10.0) / TypedValue(4.0)).Value<double>(), 2.5);
|
||||
|
||||
EXPECT_FLOAT_EQ((TypedValue(10) / TypedValue(4.0f)).Value<float>(), 2.5f);
|
||||
EXPECT_FLOAT_EQ((TypedValue(10.0f) / TypedValue(4)).Value<float>(), 2.5f);
|
||||
EXPECT_FLOAT_EQ((TypedValue(10) / TypedValue(4.0)).Value<double>(), 2.5);
|
||||
EXPECT_FLOAT_EQ((TypedValue(10.0) / TypedValue(4)).Value<double>(), 2.5);
|
||||
}
|
||||
|
||||
TEST(TypedValue, Multiplication) {
|
||||
@ -230,12 +249,10 @@ TEST(TypedValue, Multiplication) {
|
||||
false, [](const TypedValue& a, const TypedValue& b) { return a * b; });
|
||||
|
||||
EXPECT_PROP_EQ(TypedValue(10) * TypedValue(2), TypedValue(20));
|
||||
EXPECT_FLOAT_EQ((TypedValue(12.5f) * TypedValue(6.6f)).Value<float>(),
|
||||
12.5f * 6.6f);
|
||||
EXPECT_FLOAT_EQ((TypedValue(10) * TypedValue(4.5f)).Value<float>(),
|
||||
10 * 4.5f);
|
||||
EXPECT_FLOAT_EQ((TypedValue(10.2f) * TypedValue(4)).Value<float>(),
|
||||
10.2f * 4);
|
||||
EXPECT_FLOAT_EQ((TypedValue(12.5) * TypedValue(6.6)).Value<double>(),
|
||||
12.5 * 6.6);
|
||||
EXPECT_FLOAT_EQ((TypedValue(10) * TypedValue(4.5)).Value<double>(), 10 * 4.5);
|
||||
EXPECT_FLOAT_EQ((TypedValue(10.2) * TypedValue(4)).Value<double>(), 10.2 * 4);
|
||||
}
|
||||
|
||||
TEST(TypedValue, Modulo) {
|
||||
@ -245,23 +262,22 @@ TEST(TypedValue, Modulo) {
|
||||
EXPECT_PROP_EQ(TypedValue(10) % TypedValue(2), TypedValue(0));
|
||||
EXPECT_PROP_EQ(TypedValue(10) % TypedValue(4), TypedValue(2));
|
||||
|
||||
EXPECT_PROP_EQ(TypedValue(10.0f) % TypedValue(2.0f), TypedValue(0.0f));
|
||||
EXPECT_FLOAT_EQ((TypedValue(10.0f) % TypedValue(3.25f)).Value<float>(),
|
||||
0.25f);
|
||||
EXPECT_PROP_EQ(TypedValue(10.0) % TypedValue(2.0), TypedValue(0.0));
|
||||
EXPECT_FLOAT_EQ((TypedValue(10.0) % TypedValue(3.25)).Value<double>(), 0.25);
|
||||
|
||||
EXPECT_FLOAT_EQ((TypedValue(10) % TypedValue(4.0f)).Value<float>(), 2.0f);
|
||||
EXPECT_FLOAT_EQ((TypedValue(10.0f) % TypedValue(4)).Value<float>(), 2.0f);
|
||||
EXPECT_FLOAT_EQ((TypedValue(10) % TypedValue(4.0)).Value<double>(), 2.0);
|
||||
EXPECT_FLOAT_EQ((TypedValue(10.0) % TypedValue(4)).Value<double>(), 2.0);
|
||||
}
|
||||
|
||||
TEST(TypedValue, TypeIncompatibility) {
|
||||
TypedValueStore<> props = MakePropsAllTypes();
|
||||
auto props = MakePropsAllTypes();
|
||||
|
||||
// iterate over all the props, plus one, what will return
|
||||
// the Null property, which must be incompatible with all
|
||||
// the others
|
||||
for (int i = 0; i < props.size() + 1; ++i)
|
||||
for (int j = 0; j < props.size() + 1; ++j)
|
||||
EXPECT_EQ(props.at(i).type_ == props.at(j).type_, i == j);
|
||||
for (int i = 0; i < (int)props.size(); ++i)
|
||||
for (int j = 0; j < (int)props.size(); ++j)
|
||||
EXPECT_EQ(props.at(i).type() == props.at(j).type(), i == j);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -274,16 +290,16 @@ TEST(TypedValue, TypeIncompatibility) {
|
||||
*/
|
||||
void TestLogicalThrows(
|
||||
std::function<TypedValue(const TypedValue&, const TypedValue&)> op) {
|
||||
TypedValueStore<> props = MakePropsAllTypes();
|
||||
for (int i = 0; i < props.size() + 1; ++i) {
|
||||
auto props = MakePropsAllTypes();
|
||||
for (int i = 0; i < (int)props.size(); ++i) {
|
||||
auto p1 = props.at(i);
|
||||
for (int j = 0; j < props.size() + 1; ++j) {
|
||||
for (int j = 0; j < (int)props.size(); ++j) {
|
||||
auto p2 = props.at(j);
|
||||
// skip situations when both p1 and p2 are either bool or null
|
||||
auto p1_ok = p1.type_ == TypedValue::Type::Bool ||
|
||||
p1.type_ == TypedValue::Type::Null;
|
||||
auto p2_ok = p2.type_ == TypedValue::Type::Bool ||
|
||||
p2.type_ == TypedValue::Type::Null;
|
||||
auto p1_ok = p1.type() == TypedValue::Type::Bool ||
|
||||
p1.type() == TypedValue::Type::Null;
|
||||
auto p2_ok = p2.type() == TypedValue::Type::Bool ||
|
||||
p2.type() == TypedValue::Type::Null;
|
||||
if (p1_ok && p2_ok) continue;
|
||||
|
||||
EXPECT_THROW(op(p1, p2), TypedValueException);
|
||||
|
@ -1,110 +0,0 @@
|
||||
//
|
||||
// Copyright 2017 Memgraph
|
||||
// Created by Florijan Stamenkovic on 24.01.17..
|
||||
//
|
||||
#include <vector>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "storage/typed_value_store.hpp"
|
||||
|
||||
using std::string;
|
||||
|
||||
TEST(TypedValueStore, At) {
|
||||
std::string some_string = "something";
|
||||
|
||||
TypedValueStore<> props;
|
||||
EXPECT_EQ(TypedValue(props.at(0)).type_, TypedValue::Type::Null);
|
||||
props.set(0, some_string);
|
||||
EXPECT_EQ(TypedValue(props.at(0)).Value<string>(), some_string);
|
||||
props.set(120, 42);
|
||||
EXPECT_EQ(TypedValue(props.at(120)).Value<int>(), 42);
|
||||
}
|
||||
|
||||
TEST(TypedValueStore, AtNull) {
|
||||
TypedValueStore<> props;
|
||||
EXPECT_EQ(props.at(0).type_, TypedValue::Type::Null);
|
||||
EXPECT_EQ(props.at(100).type_, TypedValue::Type::Null);
|
||||
|
||||
// set one prop and test it's not null
|
||||
props.set(0, true);
|
||||
EXPECT_NE(props.at(0).type_, TypedValue::Type::Null);
|
||||
EXPECT_EQ(props.at(100).type_, TypedValue::Type::Null);
|
||||
}
|
||||
|
||||
TEST(TypedValueStore, Remove) {
|
||||
// set some props
|
||||
TypedValueStore<> props;
|
||||
props.set(11, "a");
|
||||
props.set(30, "b");
|
||||
EXPECT_NE(props.at(11).type_, TypedValue::Type::Null);
|
||||
EXPECT_NE(props.at(30).type_, TypedValue::Type::Null);
|
||||
EXPECT_EQ(props.size(), 2);
|
||||
|
||||
props.erase(11);
|
||||
EXPECT_EQ(props.size(), 1);
|
||||
EXPECT_EQ(props.at(11).type_, TypedValue::Type::Null);
|
||||
|
||||
EXPECT_EQ(props.erase(30), 1);
|
||||
EXPECT_EQ(props.size(), 0);
|
||||
EXPECT_EQ(props.at(30).type_, TypedValue::Type::Null);
|
||||
|
||||
EXPECT_EQ(props.erase(1000), 0);
|
||||
}
|
||||
|
||||
TEST(TypedValueStore, Replace) {
|
||||
TypedValueStore<> props;
|
||||
props.set(10, 42);
|
||||
EXPECT_EQ(props.at(10).Value<int>(), 42);
|
||||
props.set(10, 0.25f);
|
||||
EXPECT_EQ(props.at(10).type_, TypedValue::Type::Float);
|
||||
EXPECT_FLOAT_EQ(props.at(10).Value<float>(), 0.25);
|
||||
}
|
||||
|
||||
TEST(TypedValueStore, Size) {
|
||||
TypedValueStore<> props;
|
||||
EXPECT_EQ(props.size(), 0);
|
||||
|
||||
props.set(0, "something");
|
||||
EXPECT_EQ(props.size(), 1);
|
||||
props.set(0, true);
|
||||
EXPECT_EQ(props.size(), 1);
|
||||
props.set(1, true);
|
||||
EXPECT_EQ(props.size(), 2);
|
||||
|
||||
for (int i = 0; i < 100; ++i) props.set(i + 20, true);
|
||||
EXPECT_EQ(props.size(), 102);
|
||||
|
||||
props.erase(0);
|
||||
EXPECT_EQ(props.size(), 101);
|
||||
props.erase(0);
|
||||
EXPECT_EQ(props.size(), 101);
|
||||
props.erase(1);
|
||||
EXPECT_EQ(props.size(), 100);
|
||||
}
|
||||
|
||||
TEST(TypedValueStore, Accept) {
|
||||
int count_props = 0;
|
||||
int count_finish = 0;
|
||||
|
||||
auto handler = [&](const uint32_t key, const TypedValue& prop) {
|
||||
count_props += 1;
|
||||
};
|
||||
auto finish = [&]() { count_finish += 1; };
|
||||
|
||||
TypedValueStore<uint32_t> props;
|
||||
props.Accept(handler, finish);
|
||||
EXPECT_EQ(count_props, 0);
|
||||
EXPECT_EQ(count_finish, 1);
|
||||
|
||||
props.Accept(handler);
|
||||
EXPECT_EQ(count_props, 0);
|
||||
EXPECT_EQ(count_finish, 1);
|
||||
|
||||
props.set(0, 20);
|
||||
props.set(1, "bla");
|
||||
|
||||
props.Accept(handler, finish);
|
||||
EXPECT_EQ(count_props, 2);
|
||||
EXPECT_EQ(count_finish, 2);
|
||||
}
|
Loading…
Reference in New Issue
Block a user