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:
Mislav Bradac 2017-02-28 19:38:57 +01:00
parent b5274cd139
commit ccdffa1d7e
49 changed files with 1246 additions and 780 deletions

View File

@ -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
)
# -----------------------------------------------------------------------------

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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(); }

View File

@ -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));
}

View File

@ -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;
};

View 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());
}

View File

@ -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_;
};
/**

View File

@ -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;
/**

View File

@ -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.

View File

@ -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());

View File

@ -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_;
};

View 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");
}

View 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);

View File

@ -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_;
};

View File

@ -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;
}

View File

@ -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);

View File

@ -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;

View File

@ -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_);
}

View File

@ -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_;
};

View File

@ -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);
}

View File

@ -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) {

View File

@ -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);

View File

@ -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));

View File

@ -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));

View File

@ -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));

View File

@ -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"));

View File

@ -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"));

View File

@ -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"));

View File

@ -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();

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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");
}

View File

@ -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");
}

View File

@ -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");
}

View File

@ -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");
}

View File

@ -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");
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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()) {

View File

@ -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);
}

View File

@ -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()) {

View File

@ -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;

View File

@ -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) {

View 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);
}

View File

@ -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) {

View File

@ -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]);
}
}

View File

@ -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);

View File

@ -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);
}