diff --git a/CMakeLists.txt b/CMakeLists.txt index 5908abd4d..d64df59ec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 ) # ----------------------------------------------------------------------------- diff --git a/src/communication/bolt/v1/serialization/bolt_serializer.cpp b/src/communication/bolt/v1/serialization/bolt_serializer.cpp index 9a5fb8b25..f059ff5fa 100644 --- a/src/communication/bolt/v1/serialization/bolt_serializer.cpp +++ b/src/communication/bolt/v1/serialization/bolt_serializer.cpp @@ -6,7 +6,8 @@ #include "io/network/socket.hpp" #include "database/graph_db.hpp" -#include "storage/typed_value_store.hpp" +#include "storage/property_value_store.hpp" +#include <cassert> template <class Stream> void bolt::BoltSerializer<Stream>::write(const VertexAccessor &vertex) { @@ -27,13 +28,13 @@ void bolt::BoltSerializer<Stream>::write(const VertexAccessor &vertex) { encoder.write_string(vertex.db_accessor().label_name(label)); // write the properties - const TypedValueStore<GraphDb::Property> &props = vertex.Properties(); + const PropertyValueStore<GraphDb::Property> &props = vertex.Properties(); encoder.write_map_header(props.size()); - props.Accept( - [this, &vertex](const GraphDb::Property prop, const TypedValue &value) { - this->encoder.write(vertex.db_accessor().property_name(prop)); - this->write(value); - }); + props.Accept([this, &vertex](const GraphDb::Property prop, + const PropertyValue &value) { + this->encoder.write(vertex.db_accessor().property_name(prop)); + this->write(value); + }); } template <class Stream> @@ -54,32 +55,36 @@ void bolt::BoltSerializer<Stream>::write(const EdgeAccessor &edge) { encoder.write(edge.db_accessor().edge_type_name(edge.edge_type())); // write the property map - const TypedValueStore<GraphDb::Property> &props = edge.Properties(); + const PropertyValueStore<GraphDb::Property> &props = edge.Properties(); encoder.write_map_header(props.size()); - props.Accept([this, &edge](GraphDb::Property prop, const TypedValue &value) { - this->encoder.write(edge.db_accessor().property_name(prop)); - this->write(value); - }); + props.Accept( + [this, &edge](GraphDb::Property prop, const PropertyValue &value) { + this->encoder.write(edge.db_accessor().property_name(prop)); + this->write(value); + }); } template <class Stream> -void bolt::BoltSerializer<Stream>::write(const TypedValue &value) { - switch (value.type_) { - case TypedValue::Type::Null: +void bolt::BoltSerializer<Stream>::write(const PropertyValue &value) { + switch (value.type()) { + case PropertyValue::Type::Null: encoder.write_null(); return; - case TypedValue::Type::Bool: + case PropertyValue::Type::Bool: encoder.write_bool(value.Value<bool>()); return; - case TypedValue::Type::String: + case PropertyValue::Type::String: encoder.write_string(value.Value<std::string>()); return; - case TypedValue::Type::Int: + case PropertyValue::Type::Int: encoder.write_integer(value.Value<int>()); return; - case TypedValue::Type::Float: - encoder.write_double(value.Value<float>()); + case PropertyValue::Type::Double: + encoder.write_double(value.Value<double>()); return; + case PropertyValue::Type::List: + // Not implemented + assert(false); } } diff --git a/src/communication/bolt/v1/serialization/bolt_serializer.hpp b/src/communication/bolt/v1/serialization/bolt_serializer.hpp index a05102177..574af2085 100644 --- a/src/communication/bolt/v1/serialization/bolt_serializer.hpp +++ b/src/communication/bolt/v1/serialization/bolt_serializer.hpp @@ -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; diff --git a/src/communication/bolt/v1/serialization/record_stream.hpp b/src/communication/bolt/v1/serialization/record_stream.hpp index e7cfb332d..cf5b0ff6e 100644 --- a/src/communication/bolt/v1/serialization/record_stream.hpp +++ b/src/communication/bolt/v1/serialization/record_stream.hpp @@ -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(); } diff --git a/src/communication/bolt/v1/serialization/record_stream_mocker.hpp b/src/communication/bolt/v1/serialization/record_stream_mocker.hpp index 4caa10eea..257ba0beb 100644 --- a/src/communication/bolt/v1/serialization/record_stream_mocker.hpp +++ b/src/communication/bolt/v1/serialization/record_stream_mocker.hpp @@ -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)); } diff --git a/src/import/element_skeleton.hpp b/src/import/element_skeleton.hpp index c14fb1b6d..ffb1e8279 100644 --- a/src/import/element_skeleton.hpp +++ b/src/import/element_skeleton.hpp @@ -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; }; diff --git a/src/query/backend/cpp/typed_value.cpp b/src/query/backend/cpp/typed_value.cpp new file mode 100644 index 000000000..2574e43eb --- /dev/null +++ b/src/query/backend/cpp/typed_value.cpp @@ -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()); +} diff --git a/src/storage/typed_value.hpp b/src/query/backend/cpp/typed_value.hpp similarity index 73% rename from src/storage/typed_value.hpp rename to src/query/backend/cpp/typed_value.hpp index 49c878e21..77c0dc6f1 100644 --- a/src/storage/typed_value.hpp +++ b/src/query/backend/cpp/typed_value.hpp @@ -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_; }; /** diff --git a/src/query/plan_interface.hpp b/src/query/plan_interface.hpp index 66eaa490b..4afeb01b1 100644 --- a/src/query/plan_interface.hpp +++ b/src/query/plan_interface.hpp @@ -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; /** diff --git a/src/query/stripped.hpp b/src/query/stripped.hpp index ec2a2935a..24f0d3b2c 100644 --- a/src/query/stripped.hpp +++ b/src/query/stripped.hpp @@ -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. diff --git a/src/query/stripper.hpp b/src/query/stripper.hpp index dcff02b66..464186ebf 100644 --- a/src/query/stripper.hpp +++ b/src/query/stripper.hpp @@ -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()); diff --git a/src/storage/edge.hpp b/src/storage/edge.hpp index 0d202f2ae..e6a3a816e 100644 --- a/src/storage/edge.hpp +++ b/src/storage/edge.hpp @@ -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_; }; diff --git a/src/storage/property_value.cpp b/src/storage/property_value.cpp new file mode 100644 index 000000000..be7ac014f --- /dev/null +++ b/src/storage/property_value.cpp @@ -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"); +} diff --git a/src/storage/property_value.hpp b/src/storage/property_value.hpp new file mode 100644 index 000000000..35c19a751 --- /dev/null +++ b/src/storage/property_value.hpp @@ -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); diff --git a/src/storage/typed_value_store.hpp b/src/storage/property_value_store.hpp similarity index 76% rename from src/storage/typed_value_store.hpp rename to src/storage/property_value_store.hpp index 2efe30eab..9bde3d204 100644 --- a/src/storage/typed_value_store.hpp +++ b/src/storage/property_value_store.hpp @@ -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_; }; diff --git a/src/storage/typed_value_utils.hpp b/src/storage/property_value_utils.hpp similarity index 66% rename from src/storage/typed_value_utils.hpp rename to src/storage/property_value_utils.hpp index 46e9167fb..5f4c4fe14 100644 --- a/src/storage/typed_value_utils.hpp +++ b/src/storage/property_value_utils.hpp @@ -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; } diff --git a/src/storage/record_accessor.cpp b/src/storage/record_accessor.cpp index 999cb9ad5..acaded787 100644 --- a/src/storage/record_accessor.cpp +++ b/src/storage/record_accessor.cpp @@ -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); diff --git a/src/storage/record_accessor.hpp b/src/storage/record_accessor.hpp index 25e0cdf90..88a34e643 100644 --- a/src/storage/record_accessor.hpp +++ b/src/storage/record_accessor.hpp @@ -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; diff --git a/src/storage/typed_value.cpp b/src/storage/typed_value.cpp deleted file mode 100644 index a0cac33bd..000000000 --- a/src/storage/typed_value.cpp +++ /dev/null @@ -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_); -} diff --git a/src/storage/vertex.hpp b/src/storage/vertex.hpp index 2c09898e0..f8a39cc39 100644 --- a/src/storage/vertex.hpp +++ b/src/storage/vertex.hpp @@ -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_; }; diff --git a/tests/integration/hardcoded_query/clique.cpp b/tests/integration/hardcoded_query/clique.cpp index 39b20f740..1fa891261 100644 --- a/tests/integration/hardcoded_query/clique.cpp +++ b/tests/integration/hardcoded_query/clique.cpp @@ -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); } diff --git a/tests/integration/hardcoded_query/clique.hpp b/tests/integration/hardcoded_query/clique.hpp index a5c8d8314..5551c52aa 100644 --- a/tests/integration/hardcoded_query/clique.hpp +++ b/tests/integration/hardcoded_query/clique.hpp @@ -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) { diff --git a/tests/integration/hardcoded_query/clique_scored.cpp b/tests/integration/hardcoded_query/clique_scored.cpp index c091f7a01..cb8367dff 100644 --- a/tests/integration/hardcoded_query/clique_scored.cpp +++ b/tests/integration/hardcoded_query/clique_scored.cpp @@ -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); diff --git a/tests/integration/hardcoded_query/create_full_profile_conceals_return.cpp b/tests/integration/hardcoded_query/create_full_profile_conceals_return.cpp index 7b7097498..3b35f8892 100644 --- a/tests/integration/hardcoded_query/create_full_profile_conceals_return.cpp +++ b/tests/integration/hardcoded_query/create_full_profile_conceals_return.cpp @@ -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)); diff --git a/tests/integration/hardcoded_query/create_full_profile_return.cpp b/tests/integration/hardcoded_query/create_full_profile_return.cpp index de025c2c4..c40d6371a 100644 --- a/tests/integration/hardcoded_query/create_full_profile_return.cpp +++ b/tests/integration/hardcoded_query/create_full_profile_return.cpp @@ -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)); diff --git a/tests/integration/hardcoded_query/create_full_profile_reveals_return.cpp b/tests/integration/hardcoded_query/create_full_profile_reveals_return.cpp index 5c120a0ff..1b10d3128 100644 --- a/tests/integration/hardcoded_query/create_full_profile_reveals_return.cpp +++ b/tests/integration/hardcoded_query/create_full_profile_reveals_return.cpp @@ -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)); diff --git a/tests/integration/hardcoded_query/create_garment.cpp b/tests/integration/hardcoded_query/create_garment.cpp index 354f78e77..004724e54 100644 --- a/tests/integration/hardcoded_query/create_garment.cpp +++ b/tests/integration/hardcoded_query/create_garment.cpp @@ -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")); diff --git a/tests/integration/hardcoded_query/create_garment_conceals.cpp b/tests/integration/hardcoded_query/create_garment_conceals.cpp index b8d02fe4e..0cfaea0c1 100644 --- a/tests/integration/hardcoded_query/create_garment_conceals.cpp +++ b/tests/integration/hardcoded_query/create_garment_conceals.cpp @@ -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")); diff --git a/tests/integration/hardcoded_query/create_garment_reveals.cpp b/tests/integration/hardcoded_query/create_garment_reveals.cpp index 74c072278..50762b5ab 100644 --- a/tests/integration/hardcoded_query/create_garment_reveals.cpp +++ b/tests/integration/hardcoded_query/create_garment_reveals.cpp @@ -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")); diff --git a/tests/integration/hardcoded_query/delete_all.cpp b/tests/integration/hardcoded_query/delete_all.cpp index 5af43f6f6..83083059e 100644 --- a/tests/integration/hardcoded_query/delete_all.cpp +++ b/tests/integration/hardcoded_query/delete_all.cpp @@ -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(); diff --git a/tests/integration/hardcoded_query/match_garment.cpp b/tests/integration/hardcoded_query/match_garment.cpp index 42b4786e2..4094f94aa 100644 --- a/tests/integration/hardcoded_query/match_garment.cpp +++ b/tests/integration/hardcoded_query/match_garment.cpp @@ -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); } diff --git a/tests/integration/hardcoded_query/match_garment_default_outfit.cpp b/tests/integration/hardcoded_query/match_garment_default_outfit.cpp index 640d4550e..ef9bf1fd2 100644 --- a/tests/integration/hardcoded_query/match_garment_default_outfit.cpp +++ b/tests/integration/hardcoded_query/match_garment_default_outfit.cpp @@ -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); } diff --git a/tests/integration/hardcoded_query/match_garment_set_label_AA_return.cpp b/tests/integration/hardcoded_query/match_garment_set_label_AA_return.cpp index a487c944f..01e3cc748 100644 --- a/tests/integration/hardcoded_query/match_garment_set_label_AA_return.cpp +++ b/tests/integration/hardcoded_query/match_garment_set_label_AA_return.cpp @@ -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"); } diff --git a/tests/integration/hardcoded_query/match_garment_set_label_BB_return.cpp b/tests/integration/hardcoded_query/match_garment_set_label_BB_return.cpp index 1f320565a..94505f5ec 100644 --- a/tests/integration/hardcoded_query/match_garment_set_label_BB_return.cpp +++ b/tests/integration/hardcoded_query/match_garment_set_label_BB_return.cpp @@ -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"); } diff --git a/tests/integration/hardcoded_query/match_garment_set_label_CC_return.cpp b/tests/integration/hardcoded_query/match_garment_set_label_CC_return.cpp index e167fa18b..67f2c895f 100644 --- a/tests/integration/hardcoded_query/match_garment_set_label_CC_return.cpp +++ b/tests/integration/hardcoded_query/match_garment_set_label_CC_return.cpp @@ -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"); } diff --git a/tests/integration/hardcoded_query/match_garment_set_label_DD_return.cpp b/tests/integration/hardcoded_query/match_garment_set_label_DD_return.cpp index c3f54971a..da9ee716e 100644 --- a/tests/integration/hardcoded_query/match_garment_set_label_DD_return.cpp +++ b/tests/integration/hardcoded_query/match_garment_set_label_DD_return.cpp @@ -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"); } diff --git a/tests/integration/hardcoded_query/match_garment_set_label_EE_return.cpp b/tests/integration/hardcoded_query/match_garment_set_label_EE_return.cpp index 627ff9628..c394160c1 100644 --- a/tests/integration/hardcoded_query/match_garment_set_label_EE_return.cpp +++ b/tests/integration/hardcoded_query/match_garment_set_label_EE_return.cpp @@ -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"); } diff --git a/tests/integration/hardcoded_query/match_garment_set_label_general_return.hpp b/tests/integration/hardcoded_query/match_garment_set_label_general_return.hpp index d0f5f1b15..9b7de1006 100644 --- a/tests/integration/hardcoded_query/match_garment_set_label_general_return.hpp +++ b/tests/integration/hardcoded_query/match_garment_set_label_general_return.hpp @@ -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); diff --git a/tests/integration/hardcoded_query/match_profile.cpp b/tests/integration/hardcoded_query/match_profile.cpp index 0cb626bb4..da9d6b152 100644 --- a/tests/integration/hardcoded_query/match_profile.cpp +++ b/tests/integration/hardcoded_query/match_profile.cpp @@ -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); } diff --git a/tests/integration/hardcoded_query/match_profile_garment_score.cpp b/tests/integration/hardcoded_query/match_profile_garment_score.cpp index 95845bef8..6752ddbf8 100644 --- a/tests/integration/hardcoded_query/match_profile_garment_score.cpp +++ b/tests/integration/hardcoded_query/match_profile_garment_score.cpp @@ -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()) { diff --git a/tests/integration/hardcoded_query/match_profile_garment_set_score.cpp b/tests/integration/hardcoded_query/match_profile_garment_set_score.cpp index 4c65b268b..49142278a 100644 --- a/tests/integration/hardcoded_query/match_profile_garment_set_score.cpp +++ b/tests/integration/hardcoded_query/match_profile_garment_set_score.cpp @@ -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); } diff --git a/tests/integration/hardcoded_query/match_profile_garment_update_score.cpp b/tests/integration/hardcoded_query/match_profile_garment_update_score.cpp index 180923d9c..e69f4c1cb 100644 --- a/tests/integration/hardcoded_query/match_profile_garment_update_score.cpp +++ b/tests/integration/hardcoded_query/match_profile_garment_update_score.cpp @@ -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()) { diff --git a/tests/integration/hardcoded_query/template b/tests/integration/hardcoded_query/template index 9d234b736..1afa3a043 100644 --- a/tests/integration/hardcoded_query/template +++ b/tests/integration/hardcoded_query/template @@ -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; diff --git a/tests/integration/stream/print_record_stream.hpp b/tests/integration/stream/print_record_stream.hpp index e05309b35..42d57e91e 100644 --- a/tests/integration/stream/print_record_stream.hpp +++ b/tests/integration/stream/print_record_stream.hpp @@ -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) { diff --git a/tests/unit/property_value_store.cpp b/tests/unit/property_value_store.cpp new file mode 100644 index 000000000..9ff51c91c --- /dev/null +++ b/tests/unit/property_value_store.cpp @@ -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); +} diff --git a/tests/unit/record_edge_vertex_accessor.cpp b/tests/unit/record_edge_vertex_accessor.cpp index 1827aef58..7b455a22a 100644 --- a/tests/unit/record_edge_vertex_accessor.cpp +++ b/tests/unit/record_edge_vertex_accessor.cpp @@ -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) { diff --git a/tests/unit/record_stream_mocker.cpp b/tests/unit/record_stream_mocker.cpp index 9e8b9b855..476d6a080 100644 --- a/tests/unit/record_stream_mocker.cpp +++ b/tests/unit/record_stream_mocker.cpp @@ -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]); } } diff --git a/tests/unit/typed_value.cpp b/tests/unit/typed_value.cpp index 5bb8a905b..495cbe29d 100644 --- a/tests/unit/typed_value.cpp +++ b/tests/unit/typed_value.cpp @@ -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); diff --git a/tests/unit/typed_value_store.cpp b/tests/unit/typed_value_store.cpp deleted file mode 100644 index 12fe0bded..000000000 --- a/tests/unit/typed_value_store.cpp +++ /dev/null @@ -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); -}