Merge old and new PropertyValue implementations
Summary: This effectively replaces the old PropertyValue implementation from the one in storage/v2 Depends on D2333 Reviewers: mferencevic, ipaljak Reviewed By: mferencevic Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D2335
This commit is contained in:
parent
32ae2b4d23
commit
c8340ad120
@ -68,7 +68,6 @@ set(mg_single_node_sources
|
||||
storage/common/constraints/record.cpp
|
||||
storage/common/constraints/unique_constraints.cpp
|
||||
storage/common/locking/record_lock.cpp
|
||||
storage/common/types/property_value.cpp
|
||||
storage/common/types/property_value_store.cpp
|
||||
storage/single_node/edge_accessor.cpp
|
||||
storage/single_node/record_accessor.cpp
|
||||
@ -148,7 +147,6 @@ set(mg_single_node_ha_sources
|
||||
query/typed_value.cpp
|
||||
storage/common/constraints/record.cpp
|
||||
storage/common/constraints/unique_constraints.cpp
|
||||
storage/common/types/property_value.cpp
|
||||
storage/common/types/slk.cpp
|
||||
storage/common/types/property_value_store.cpp
|
||||
storage/common/locking/record_lock.cpp
|
||||
|
@ -1,235 +0,0 @@
|
||||
#include "storage/common/types/property_value.hpp"
|
||||
|
||||
// constructors
|
||||
PropertyValue::PropertyValue(const PropertyValue &other) : type_(other.type_) {
|
||||
switch (other.type_) {
|
||||
case Type::Null:
|
||||
return;
|
||||
case Type::Bool:
|
||||
this->bool_v = other.bool_v;
|
||||
return;
|
||||
case Type::Int:
|
||||
this->int_v = other.int_v;
|
||||
return;
|
||||
case Type::Double:
|
||||
this->double_v = other.double_v;
|
||||
return;
|
||||
case Type::String:
|
||||
new (&string_v) std::string(other.string_v);
|
||||
return;
|
||||
case Type::List:
|
||||
new (&list_v) std::vector<PropertyValue>(other.list_v);
|
||||
return;
|
||||
case Type::Map:
|
||||
new (&map_v) std::map<std::string, PropertyValue>(other.map_v);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
PropertyValue::PropertyValue(PropertyValue &&other) noexcept
|
||||
: type_(other.type_) {
|
||||
switch (other.type_) {
|
||||
case Type::Null:
|
||||
return;
|
||||
case Type::Bool:
|
||||
this->bool_v = other.bool_v;
|
||||
return;
|
||||
case Type::Int:
|
||||
this->int_v = other.int_v;
|
||||
return;
|
||||
case Type::Double:
|
||||
this->double_v = other.double_v;
|
||||
return;
|
||||
case Type::String:
|
||||
new (&string_v) std::string(std::move(other.string_v));
|
||||
return;
|
||||
case Type::List:
|
||||
new (&list_v) std::vector<PropertyValue>(std::move(other.list_v));
|
||||
return;
|
||||
case Type::Map:
|
||||
new (&map_v) std::map<std::string, PropertyValue>(std::move(other.map_v));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
PropertyValue &PropertyValue::operator=(const PropertyValue &other) {
|
||||
if (this != &other) {
|
||||
DestroyValue();
|
||||
type_ = other.type_;
|
||||
|
||||
switch (other.type_) {
|
||||
case Type::Null:
|
||||
case Type::Bool:
|
||||
this->bool_v = other.bool_v;
|
||||
break;
|
||||
case Type::Int:
|
||||
this->int_v = other.int_v;
|
||||
break;
|
||||
case Type::Double:
|
||||
this->double_v = other.double_v;
|
||||
break;
|
||||
case Type::String:
|
||||
new (&string_v) std::string(other.string_v);
|
||||
break;
|
||||
case Type::List:
|
||||
new (&list_v) std::vector<PropertyValue>(other.list_v);
|
||||
break;
|
||||
case Type::Map:
|
||||
new (&map_v) std::map<std::string, PropertyValue>(other.map_v);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
PropertyValue &PropertyValue::operator=(PropertyValue &&other) noexcept {
|
||||
if (this != &other) {
|
||||
DestroyValue();
|
||||
type_ = other.type_;
|
||||
|
||||
switch (other.type_) {
|
||||
case Type::Null:
|
||||
break;
|
||||
case Type::Bool:
|
||||
this->bool_v = other.bool_v;
|
||||
break;
|
||||
case Type::Int:
|
||||
this->int_v = other.int_v;
|
||||
break;
|
||||
case Type::Double:
|
||||
this->double_v = other.double_v;
|
||||
break;
|
||||
case Type::String:
|
||||
new (&string_v) std::string(std::move(other.string_v));
|
||||
break;
|
||||
case Type::List:
|
||||
new (&list_v) std::vector<PropertyValue>(std::move(other.list_v));
|
||||
break;
|
||||
case Type::Map:
|
||||
new (&map_v)
|
||||
std::map<std::string, PropertyValue>(std::move(other.map_v));
|
||||
break;
|
||||
}
|
||||
|
||||
// reset the type of other
|
||||
other.DestroyValue();
|
||||
other.type_ = Type::Null;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void PropertyValue::DestroyValue() {
|
||||
switch (type_) {
|
||||
// destructor for primitive types does nothing
|
||||
case Type::Null:
|
||||
case Type::Bool:
|
||||
case Type::Int:
|
||||
case Type::Double:
|
||||
return;
|
||||
|
||||
// destructor 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:
|
||||
list_v.~vector();
|
||||
return;
|
||||
case Type::Map:
|
||||
map_v.~map();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
PropertyValue::~PropertyValue() { DestroyValue(); }
|
||||
|
||||
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";
|
||||
case PropertyValue::Type::Map:
|
||||
return os << "map";
|
||||
}
|
||||
}
|
||||
|
||||
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.ValueBool() ? "true" : "false");
|
||||
case PropertyValue::Type::String:
|
||||
return os << value.ValueString();
|
||||
case PropertyValue::Type::Int:
|
||||
return os << value.ValueInt();
|
||||
case PropertyValue::Type::Double:
|
||||
return os << value.ValueDouble();
|
||||
case PropertyValue::Type::List:
|
||||
os << "[";
|
||||
for (const auto &x : value.ValueList()) {
|
||||
os << x << ",";
|
||||
}
|
||||
return os << "]";
|
||||
case PropertyValue::Type::Map:
|
||||
os << "{";
|
||||
for (const auto &kv :
|
||||
value.ValueMap()) {
|
||||
os << kv.first << ": " << kv.second << ",";
|
||||
}
|
||||
return os << "}";
|
||||
}
|
||||
}
|
||||
|
||||
bool operator==(const PropertyValue &first, const PropertyValue &second) {
|
||||
if (first.type() != second.type()) return false;
|
||||
switch (first.type()) {
|
||||
case PropertyValue::Type::Null:
|
||||
return true;
|
||||
case PropertyValue::Type::Bool:
|
||||
return first.ValueBool() == second.ValueBool();
|
||||
case PropertyValue::Type::Int:
|
||||
return first.ValueInt() == second.ValueInt();
|
||||
case PropertyValue::Type::Double:
|
||||
return first.ValueDouble() == second.ValueDouble();
|
||||
case PropertyValue::Type::String:
|
||||
return first.ValueString() == second.ValueString();
|
||||
case PropertyValue::Type::List:
|
||||
return first.ValueList() == second.ValueList();
|
||||
case PropertyValue::Type::Map:
|
||||
return first.ValueMap() == second.ValueMap();
|
||||
}
|
||||
}
|
||||
|
||||
bool operator<(const PropertyValue &first, const PropertyValue &second) {
|
||||
if (first.type() != second.type()) return first.type() < second.type();
|
||||
switch (first.type()) {
|
||||
case PropertyValue::Type::Null:
|
||||
return false;
|
||||
case PropertyValue::Type::Bool:
|
||||
return first.ValueBool() < second.ValueBool();
|
||||
case PropertyValue::Type::Int:
|
||||
return first.ValueInt() < second.ValueInt();
|
||||
case PropertyValue::Type::Double:
|
||||
return first.ValueDouble() < second.ValueDouble();
|
||||
case PropertyValue::Type::String:
|
||||
return first.ValueString() < second.ValueString();
|
||||
case PropertyValue::Type::List:
|
||||
return first.ValueList() < second.ValueList();
|
||||
case PropertyValue::Type::Map:
|
||||
return first.ValueMap() < second.ValueMap();
|
||||
}
|
||||
}
|
@ -1,182 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "storage/v2/property_value.hpp"
|
||||
|
||||
#include "utils/exceptions.hpp"
|
||||
|
||||
/**
|
||||
* An exception raised by the PropertyValue system. Typically when
|
||||
* trying to perform operations (such as addition) on PropertyValues
|
||||
* of incompatible Types.
|
||||
*/
|
||||
class PropertyValueException : public utils::StacktraceException {
|
||||
public:
|
||||
using utils::StacktraceException::StacktraceException;
|
||||
};
|
||||
|
||||
/**
|
||||
* Encapsulation of a value and its type in a class that has no compile-time
|
||||
* info about the 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 {
|
||||
public:
|
||||
/** A value type. Each type corresponds to exactly one C++ type */
|
||||
enum class Type : unsigned { Null, String, Bool, Int, Double, List, Map };
|
||||
|
||||
/** Checks if the given PropertyValue::Types are comparable */
|
||||
static bool AreComparableTypes(Type a, Type b) {
|
||||
auto is_numeric = [](const Type t) {
|
||||
return t == Type::Int || t == Type::Double;
|
||||
};
|
||||
|
||||
return a == b || (is_numeric(a) && is_numeric(b));
|
||||
}
|
||||
|
||||
// default constructor, makes Null
|
||||
PropertyValue() : type_(Type::Null) {}
|
||||
|
||||
// constructors for primitive types
|
||||
explicit PropertyValue(bool value) : type_(Type::Bool) { bool_v = value; }
|
||||
explicit PropertyValue(int value) : type_(Type::Int) { int_v = value; }
|
||||
explicit PropertyValue(int64_t value) : type_(Type::Int) { int_v = value; }
|
||||
explicit PropertyValue(double value) : type_(Type::Double) {
|
||||
double_v = value;
|
||||
}
|
||||
|
||||
// constructors for non-primitive types
|
||||
explicit PropertyValue(const std::string &value) : type_(Type::String) {
|
||||
new (&string_v) std::string(value);
|
||||
}
|
||||
explicit PropertyValue(const char *value) : type_(Type::String) {
|
||||
new (&string_v) std::string(value);
|
||||
}
|
||||
explicit PropertyValue(const std::vector<PropertyValue> &value)
|
||||
: type_(Type::List) {
|
||||
new (&list_v) std::vector<PropertyValue>(value);
|
||||
}
|
||||
explicit PropertyValue(const std::map<std::string, PropertyValue> &value)
|
||||
: type_(Type::Map) {
|
||||
new (&map_v) std::map<std::string, PropertyValue>(value);
|
||||
}
|
||||
|
||||
// move constructors for non-primitive types
|
||||
explicit PropertyValue(std::string &&value) noexcept : type_(Type::String) {
|
||||
new (&string_v) std::string(std::move(value));
|
||||
}
|
||||
explicit PropertyValue(std::vector<PropertyValue> &&value) noexcept
|
||||
: type_(Type::List) {
|
||||
new (&list_v) std::vector<PropertyValue>(std::move(value));
|
||||
}
|
||||
explicit PropertyValue(std::map<std::string, PropertyValue> &&value) noexcept
|
||||
: type_(Type::Map) {
|
||||
new (&map_v) std::map<std::string, PropertyValue>(std::move(value));
|
||||
}
|
||||
|
||||
PropertyValue &operator=(const PropertyValue &other);
|
||||
PropertyValue &operator=(PropertyValue &&other) noexcept;
|
||||
|
||||
PropertyValue(const PropertyValue &other);
|
||||
PropertyValue(PropertyValue &&other) noexcept;
|
||||
~PropertyValue();
|
||||
|
||||
Type type() const { return type_; }
|
||||
|
||||
bool IsNull() const { return type_ == Type::Null; }
|
||||
|
||||
bool ValueBool() const {
|
||||
if (type_ != Type::Bool) {
|
||||
throw PropertyValueException("This value isn't a bool!");
|
||||
}
|
||||
return bool_v;
|
||||
}
|
||||
|
||||
int64_t ValueInt() const {
|
||||
if (type_ != Type::Int) {
|
||||
throw PropertyValueException("This value isn't a int!");
|
||||
}
|
||||
return int_v;
|
||||
}
|
||||
|
||||
double ValueDouble() const {
|
||||
if (type_ != Type::Double) {
|
||||
throw PropertyValueException("This value isn't a double!");
|
||||
}
|
||||
return double_v;
|
||||
}
|
||||
|
||||
const std::string &ValueString() const {
|
||||
if (type_ != Type::String) {
|
||||
throw PropertyValueException("The value isn't a string!");
|
||||
}
|
||||
return string_v;
|
||||
}
|
||||
|
||||
const std::vector<PropertyValue> &ValueList() const {
|
||||
if (type_ != Type::List) {
|
||||
throw PropertyValueException("The value isn't a list!");
|
||||
}
|
||||
return list_v;
|
||||
}
|
||||
|
||||
const std::map<std::string, PropertyValue> &ValueMap() const {
|
||||
if (type_ != Type::Map) {
|
||||
throw PropertyValueException("The value isn't a map!");
|
||||
}
|
||||
return map_v;
|
||||
}
|
||||
|
||||
std::string &ValueString() {
|
||||
if (type_ != Type::String) {
|
||||
throw PropertyValueException("The value isn't a string!");
|
||||
}
|
||||
return string_v;
|
||||
}
|
||||
|
||||
std::vector<PropertyValue> &ValueList() {
|
||||
if (type_ != Type::List) {
|
||||
throw PropertyValueException("The value isn't a list!");
|
||||
}
|
||||
return list_v;
|
||||
}
|
||||
|
||||
std::map<std::string, PropertyValue> &ValueMap() {
|
||||
if (type_ != Type::Map) {
|
||||
throw PropertyValueException("The value isn't a map!");
|
||||
}
|
||||
return map_v;
|
||||
}
|
||||
|
||||
private:
|
||||
void DestroyValue();
|
||||
|
||||
// storage for the value of the property
|
||||
union {
|
||||
bool bool_v;
|
||||
int64_t int_v;
|
||||
double double_v;
|
||||
std::string string_v;
|
||||
// We support lists of values of different types, neo4j supports lists of
|
||||
// values of the same type.
|
||||
std::vector<PropertyValue> list_v;
|
||||
std::map<std::string, PropertyValue> map_v;
|
||||
};
|
||||
|
||||
/**
|
||||
* The Type of property.
|
||||
*/
|
||||
Type type_;
|
||||
};
|
||||
|
||||
// stream output
|
||||
std::ostream &operator<<(std::ostream &os, const PropertyValue::Type type);
|
||||
std::ostream &operator<<(std::ostream &os, const PropertyValue &value);
|
||||
|
||||
// comparison
|
||||
bool operator==(const PropertyValue &first, const PropertyValue &second);
|
||||
bool operator<(const PropertyValue &first, const PropertyValue &second);
|
||||
using storage::PropertyValue;
|
||||
using storage::PropertyValueException;
|
||||
|
@ -259,13 +259,13 @@ class LabelPropertyIndex {
|
||||
// upper bound and value type
|
||||
std::function<bool(const IndexEntry &entry)> predicate;
|
||||
if (lower && upper &&
|
||||
!PropertyValue::AreComparableTypes(type(lower), type(upper)))
|
||||
!AreComparablePropertyValueTypes(type(lower), type(upper)))
|
||||
predicate = [](const IndexEntry &) { return false; };
|
||||
else if (upper) {
|
||||
auto upper_index_entry =
|
||||
make_index_bound(upper, upper.value().IsExclusive());
|
||||
predicate = [upper_index_entry](const IndexEntry &entry) {
|
||||
return PropertyValue::AreComparableTypes(
|
||||
return AreComparablePropertyValueTypes(
|
||||
entry.value_.type(), upper_index_entry.value_.type()) &&
|
||||
entry < upper_index_entry;
|
||||
};
|
||||
@ -273,8 +273,7 @@ class LabelPropertyIndex {
|
||||
auto lower_type = type(lower);
|
||||
make_index_bound(lower, lower.value().IsExclusive());
|
||||
predicate = [lower_type](const IndexEntry &entry) {
|
||||
return PropertyValue::AreComparableTypes(entry.value_.type(),
|
||||
lower_type);
|
||||
return AreComparablePropertyValueTypes(entry.value_.type(), lower_type);
|
||||
};
|
||||
}
|
||||
|
||||
@ -367,6 +366,15 @@ class LabelPropertyIndex {
|
||||
}
|
||||
|
||||
private:
|
||||
static bool AreComparablePropertyValueTypes(PropertyValue::Type a,
|
||||
PropertyValue::Type b) {
|
||||
auto is_numeric = [](const PropertyValue::Type t) {
|
||||
return t == PropertyValue::Type::Int || t == PropertyValue::Type::Double;
|
||||
};
|
||||
|
||||
return a == b || (is_numeric(a) && is_numeric(b));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief - Contains value, vlist and vertex record to distinguish between
|
||||
* index entries.
|
||||
@ -401,7 +409,7 @@ class LabelPropertyIndex {
|
||||
* than the second one
|
||||
*/
|
||||
static bool Less(const PropertyValue &a, const PropertyValue &b) {
|
||||
if (!PropertyValue::AreComparableTypes(a.type(), b.type()))
|
||||
if (!AreComparablePropertyValueTypes(a.type(), b.type()))
|
||||
return a.type() < b.type();
|
||||
|
||||
if (a.type() == b.type()) {
|
||||
|
@ -259,13 +259,13 @@ class LabelPropertyIndex {
|
||||
// upper bound and value type
|
||||
std::function<bool(const IndexEntry &entry)> predicate;
|
||||
if (lower && upper &&
|
||||
!PropertyValue::AreComparableTypes(type(lower), type(upper)))
|
||||
!AreComparablePropertyValueTypes(type(lower), type(upper)))
|
||||
predicate = [](const IndexEntry &) { return false; };
|
||||
else if (upper) {
|
||||
auto upper_index_entry =
|
||||
make_index_bound(upper, upper.value().IsExclusive());
|
||||
predicate = [upper_index_entry](const IndexEntry &entry) {
|
||||
return PropertyValue::AreComparableTypes(
|
||||
return AreComparablePropertyValueTypes(
|
||||
entry.value_.type(), upper_index_entry.value_.type()) &&
|
||||
entry < upper_index_entry;
|
||||
};
|
||||
@ -273,8 +273,7 @@ class LabelPropertyIndex {
|
||||
auto lower_type = type(lower);
|
||||
make_index_bound(lower, lower.value().IsExclusive());
|
||||
predicate = [lower_type](const IndexEntry &entry) {
|
||||
return PropertyValue::AreComparableTypes(entry.value_.type(),
|
||||
lower_type);
|
||||
return AreComparablePropertyValueTypes(entry.value_.type(), lower_type);
|
||||
};
|
||||
}
|
||||
|
||||
@ -367,6 +366,15 @@ class LabelPropertyIndex {
|
||||
}
|
||||
|
||||
private:
|
||||
static bool AreComparablePropertyValueTypes(PropertyValue::Type a,
|
||||
PropertyValue::Type b) {
|
||||
auto is_numeric = [](const PropertyValue::Type t) {
|
||||
return t == PropertyValue::Type::Int || t == PropertyValue::Type::Double;
|
||||
};
|
||||
|
||||
return a == b || (is_numeric(a) && is_numeric(b));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief - Contains value, vlist and vertex record to distinguish between
|
||||
* index entries.
|
||||
@ -401,7 +409,7 @@ class LabelPropertyIndex {
|
||||
* than the second one
|
||||
*/
|
||||
static bool Less(const PropertyValue &a, const PropertyValue &b) {
|
||||
if (!PropertyValue::AreComparableTypes(a.type(), b.type()))
|
||||
if (!AreComparablePropertyValueTypes(a.type(), b.type()))
|
||||
return a.type() < b.type();
|
||||
|
||||
if (a.type() == b.type()) {
|
||||
|
@ -24,9 +24,10 @@ class PropertyValueException : public utils::BasicException {
|
||||
/// PropertyValue::Type. Each such type corresponds to exactly one C++ type.
|
||||
class PropertyValue {
|
||||
public:
|
||||
enum class Type : uint8_t { Null, Bool, Int, Double, String, List, Map };
|
||||
/// A value type, each type corresponds to exactly one C++ type.
|
||||
enum class Type : unsigned char { Null, Bool, Int, Double, String, List, Map };
|
||||
|
||||
// default constructor, makes Null
|
||||
/// Make a Null value
|
||||
PropertyValue() : type_(Type::Null) {}
|
||||
|
||||
// constructors for primitive types
|
||||
@ -67,133 +68,16 @@ class PropertyValue {
|
||||
}
|
||||
|
||||
// copy constructor
|
||||
PropertyValue(const PropertyValue &other) : type_(other.type_) {
|
||||
switch (other.type_) {
|
||||
case Type::Null:
|
||||
return;
|
||||
case Type::Bool:
|
||||
this->bool_v = other.bool_v;
|
||||
return;
|
||||
case Type::Int:
|
||||
this->int_v = other.int_v;
|
||||
return;
|
||||
case Type::Double:
|
||||
this->double_v = other.double_v;
|
||||
return;
|
||||
case Type::String:
|
||||
new (&string_v) std::string(other.string_v);
|
||||
return;
|
||||
case Type::List:
|
||||
new (&list_v) std::vector<PropertyValue>(other.list_v);
|
||||
return;
|
||||
case Type::Map:
|
||||
new (&map_v) std::map<std::string, PropertyValue>(other.map_v);
|
||||
return;
|
||||
}
|
||||
}
|
||||
PropertyValue(const PropertyValue &other);
|
||||
|
||||
// move constructor
|
||||
PropertyValue(PropertyValue &&other) noexcept : type_(other.type_) {
|
||||
switch (other.type_) {
|
||||
case Type::Null:
|
||||
break;
|
||||
case Type::Bool:
|
||||
this->bool_v = other.bool_v;
|
||||
break;
|
||||
case Type::Int:
|
||||
this->int_v = other.int_v;
|
||||
break;
|
||||
case Type::Double:
|
||||
this->double_v = other.double_v;
|
||||
break;
|
||||
case Type::String:
|
||||
new (&string_v) std::string(std::move(other.string_v));
|
||||
break;
|
||||
case Type::List:
|
||||
new (&list_v) std::vector<PropertyValue>(std::move(other.list_v));
|
||||
break;
|
||||
case Type::Map:
|
||||
new (&map_v)
|
||||
std::map<std::string, PropertyValue>(std::move(other.map_v));
|
||||
break;
|
||||
}
|
||||
|
||||
// reset the type of other
|
||||
other.DestroyValue();
|
||||
other.type_ = Type::Null;
|
||||
}
|
||||
PropertyValue(PropertyValue &&other) noexcept;
|
||||
|
||||
// copy assignment
|
||||
PropertyValue &operator=(const PropertyValue &other) {
|
||||
if (this == &other) return *this;
|
||||
|
||||
DestroyValue();
|
||||
type_ = other.type_;
|
||||
|
||||
switch (other.type_) {
|
||||
case Type::Null:
|
||||
break;
|
||||
case Type::Bool:
|
||||
this->bool_v = other.bool_v;
|
||||
break;
|
||||
case Type::Int:
|
||||
this->int_v = other.int_v;
|
||||
break;
|
||||
case Type::Double:
|
||||
this->double_v = other.double_v;
|
||||
break;
|
||||
case Type::String:
|
||||
new (&string_v) std::string(other.string_v);
|
||||
break;
|
||||
case Type::List:
|
||||
new (&list_v) std::vector<PropertyValue>(other.list_v);
|
||||
break;
|
||||
case Type::Map:
|
||||
new (&map_v) std::map<std::string, PropertyValue>(other.map_v);
|
||||
break;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
PropertyValue &operator=(const PropertyValue &other);
|
||||
|
||||
// move assignment
|
||||
PropertyValue &operator=(PropertyValue &&other) noexcept {
|
||||
if (this == &other) return *this;
|
||||
|
||||
DestroyValue();
|
||||
type_ = other.type_;
|
||||
|
||||
switch (other.type_) {
|
||||
case Type::Null:
|
||||
break;
|
||||
case Type::Bool:
|
||||
this->bool_v = other.bool_v;
|
||||
break;
|
||||
case Type::Int:
|
||||
this->int_v = other.int_v;
|
||||
break;
|
||||
case Type::Double:
|
||||
this->double_v = other.double_v;
|
||||
break;
|
||||
case Type::String:
|
||||
new (&string_v) std::string(std::move(other.string_v));
|
||||
break;
|
||||
case Type::List:
|
||||
new (&list_v) std::vector<PropertyValue>(std::move(other.list_v));
|
||||
break;
|
||||
case Type::Map:
|
||||
new (&map_v)
|
||||
std::map<std::string, PropertyValue>(std::move(other.map_v));
|
||||
break;
|
||||
}
|
||||
|
||||
// reset the type of other
|
||||
other.DestroyValue();
|
||||
other.type_ = Type::Null;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
PropertyValue &operator=(PropertyValue &&other) noexcept;
|
||||
// TODO: Implement copy assignment operators for primitive types.
|
||||
// TODO: Implement copy and move assignment operators for non-primitive types.
|
||||
|
||||
@ -237,12 +121,14 @@ class PropertyValue {
|
||||
}
|
||||
return string_v;
|
||||
}
|
||||
|
||||
const std::vector<PropertyValue> &ValueList() const {
|
||||
if (type_ != Type::List) {
|
||||
throw PropertyValueException("The value isn't a list!");
|
||||
}
|
||||
return list_v;
|
||||
}
|
||||
|
||||
const std::map<std::string, PropertyValue> &ValueMap() const {
|
||||
if (type_ != Type::Map) {
|
||||
throw PropertyValueException("The value isn't a map!");
|
||||
@ -257,12 +143,14 @@ class PropertyValue {
|
||||
}
|
||||
return string_v;
|
||||
}
|
||||
|
||||
std::vector<PropertyValue> &ValueList() {
|
||||
if (type_ != Type::List) {
|
||||
throw PropertyValueException("The value isn't a list!");
|
||||
}
|
||||
return list_v;
|
||||
}
|
||||
|
||||
std::map<std::string, PropertyValue> &ValueMap() {
|
||||
if (type_ != Type::Map) {
|
||||
throw PropertyValueException("The value isn't a map!");
|
||||
@ -271,30 +159,7 @@ class PropertyValue {
|
||||
}
|
||||
|
||||
private:
|
||||
void DestroyValue() {
|
||||
switch (type_) {
|
||||
// destructor for primitive types does nothing
|
||||
case Type::Null:
|
||||
case Type::Bool:
|
||||
case Type::Int:
|
||||
case Type::Double:
|
||||
return;
|
||||
|
||||
// destructor 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:
|
||||
list_v.~vector();
|
||||
return;
|
||||
case Type::Map:
|
||||
map_v.~map();
|
||||
return;
|
||||
}
|
||||
}
|
||||
void DestroyValue() noexcept;
|
||||
|
||||
union {
|
||||
bool bool_v;
|
||||
@ -395,4 +260,153 @@ inline bool operator<(const PropertyValue &first, const PropertyValue &second) {
|
||||
}
|
||||
}
|
||||
|
||||
inline PropertyValue::PropertyValue(const PropertyValue &other)
|
||||
: type_(other.type_) {
|
||||
switch (other.type_) {
|
||||
case Type::Null:
|
||||
return;
|
||||
case Type::Bool:
|
||||
this->bool_v = other.bool_v;
|
||||
return;
|
||||
case Type::Int:
|
||||
this->int_v = other.int_v;
|
||||
return;
|
||||
case Type::Double:
|
||||
this->double_v = other.double_v;
|
||||
return;
|
||||
case Type::String:
|
||||
new (&string_v) std::string(other.string_v);
|
||||
return;
|
||||
case Type::List:
|
||||
new (&list_v) std::vector<PropertyValue>(other.list_v);
|
||||
return;
|
||||
case Type::Map:
|
||||
new (&map_v) std::map<std::string, PropertyValue>(other.map_v);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
inline PropertyValue::PropertyValue(PropertyValue &&other) noexcept
|
||||
: type_(other.type_) {
|
||||
switch (other.type_) {
|
||||
case Type::Null:
|
||||
break;
|
||||
case Type::Bool:
|
||||
this->bool_v = other.bool_v;
|
||||
break;
|
||||
case Type::Int:
|
||||
this->int_v = other.int_v;
|
||||
break;
|
||||
case Type::Double:
|
||||
this->double_v = other.double_v;
|
||||
break;
|
||||
case Type::String:
|
||||
new (&string_v) std::string(std::move(other.string_v));
|
||||
break;
|
||||
case Type::List:
|
||||
new (&list_v) std::vector<PropertyValue>(std::move(other.list_v));
|
||||
break;
|
||||
case Type::Map:
|
||||
new (&map_v) std::map<std::string, PropertyValue>(std::move(other.map_v));
|
||||
break;
|
||||
}
|
||||
|
||||
// reset the type of other
|
||||
other.DestroyValue();
|
||||
other.type_ = Type::Null;
|
||||
}
|
||||
|
||||
inline PropertyValue &PropertyValue::operator=(const PropertyValue &other) {
|
||||
if (this == &other) return *this;
|
||||
|
||||
DestroyValue();
|
||||
type_ = other.type_;
|
||||
|
||||
switch (other.type_) {
|
||||
case Type::Null:
|
||||
break;
|
||||
case Type::Bool:
|
||||
this->bool_v = other.bool_v;
|
||||
break;
|
||||
case Type::Int:
|
||||
this->int_v = other.int_v;
|
||||
break;
|
||||
case Type::Double:
|
||||
this->double_v = other.double_v;
|
||||
break;
|
||||
case Type::String:
|
||||
new (&string_v) std::string(other.string_v);
|
||||
break;
|
||||
case Type::List:
|
||||
new (&list_v) std::vector<PropertyValue>(other.list_v);
|
||||
break;
|
||||
case Type::Map:
|
||||
new (&map_v) std::map<std::string, PropertyValue>(other.map_v);
|
||||
break;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline PropertyValue &PropertyValue::operator=(PropertyValue &&other) noexcept {
|
||||
if (this == &other) return *this;
|
||||
|
||||
DestroyValue();
|
||||
type_ = other.type_;
|
||||
|
||||
switch (other.type_) {
|
||||
case Type::Null:
|
||||
break;
|
||||
case Type::Bool:
|
||||
this->bool_v = other.bool_v;
|
||||
break;
|
||||
case Type::Int:
|
||||
this->int_v = other.int_v;
|
||||
break;
|
||||
case Type::Double:
|
||||
this->double_v = other.double_v;
|
||||
break;
|
||||
case Type::String:
|
||||
new (&string_v) std::string(std::move(other.string_v));
|
||||
break;
|
||||
case Type::List:
|
||||
new (&list_v) std::vector<PropertyValue>(std::move(other.list_v));
|
||||
break;
|
||||
case Type::Map:
|
||||
new (&map_v) std::map<std::string, PropertyValue>(std::move(other.map_v));
|
||||
break;
|
||||
}
|
||||
|
||||
// reset the type of other
|
||||
other.DestroyValue();
|
||||
other.type_ = Type::Null;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline void PropertyValue::DestroyValue() noexcept {
|
||||
switch (type_) {
|
||||
// destructor for primitive types does nothing
|
||||
case Type::Null:
|
||||
case Type::Bool:
|
||||
case Type::Int:
|
||||
case Type::Double:
|
||||
return;
|
||||
|
||||
// destructor 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:
|
||||
list_v.~vector();
|
||||
return;
|
||||
case Type::Map:
|
||||
map_v.~map();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace storage
|
||||
|
@ -254,19 +254,12 @@ TEST_F(GraphDbAccessorIndex, LabelPropertyValueSorting) {
|
||||
|
||||
std::vector<PropertyValue> expected_property_value(50, PropertyValue(0));
|
||||
|
||||
// strings
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
auto vertex_accessor = dba.InsertVertex();
|
||||
vertex_accessor.add_label(label);
|
||||
vertex_accessor.PropsSet(property, PropertyValue(std::to_string(i)));
|
||||
expected_property_value[i] = vertex_accessor.PropsAt(property);
|
||||
}
|
||||
// bools - insert in reverse to check for comparison between values.
|
||||
for (int i = 9; i >= 0; --i) {
|
||||
auto vertex_accessor = dba.InsertVertex();
|
||||
vertex_accessor.add_label(label);
|
||||
vertex_accessor.PropsSet(property, PropertyValue(static_cast<bool>(i / 5)));
|
||||
expected_property_value[10 + i] = vertex_accessor.PropsAt(property);
|
||||
expected_property_value[i] = vertex_accessor.PropsAt(property);
|
||||
}
|
||||
|
||||
// integers
|
||||
@ -274,7 +267,7 @@ TEST_F(GraphDbAccessorIndex, LabelPropertyValueSorting) {
|
||||
auto vertex_accessor = dba.InsertVertex();
|
||||
vertex_accessor.add_label(label);
|
||||
vertex_accessor.PropsSet(property, PropertyValue(i));
|
||||
expected_property_value[20 + 2 * i] = vertex_accessor.PropsAt(property);
|
||||
expected_property_value[10 + 2 * i] = vertex_accessor.PropsAt(property);
|
||||
}
|
||||
// doubles
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
@ -282,7 +275,15 @@ TEST_F(GraphDbAccessorIndex, LabelPropertyValueSorting) {
|
||||
vertex_accessor.add_label(label);
|
||||
vertex_accessor.PropsSet(property,
|
||||
PropertyValue(static_cast<double>(i + 0.5)));
|
||||
expected_property_value[20 + 2 * i + 1] = vertex_accessor.PropsAt(property);
|
||||
expected_property_value[10 + 2 * i + 1] = vertex_accessor.PropsAt(property);
|
||||
}
|
||||
|
||||
// strings
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
auto vertex_accessor = dba.InsertVertex();
|
||||
vertex_accessor.add_label(label);
|
||||
vertex_accessor.PropsSet(property, PropertyValue(std::to_string(i)));
|
||||
expected_property_value[30 + i] = vertex_accessor.PropsAt(property);
|
||||
}
|
||||
|
||||
// lists of ints - insert in reverse to check for comparision between
|
||||
|
@ -1663,12 +1663,19 @@ TEST(QueryPlan, ScanAllByLabelProperty) {
|
||||
Bound::Type::INCLUSIVE,
|
||||
{TypedValue(std::vector<TypedValue>{TypedValue(1)})});
|
||||
|
||||
auto are_comparable = [](PropertyValue::Type a, PropertyValue::Type b) {
|
||||
auto is_numeric = [](const PropertyValue::Type t) {
|
||||
return t == PropertyValue::Type::Int || t == PropertyValue::Type::Double;
|
||||
};
|
||||
|
||||
return a == b || (is_numeric(a) && is_numeric(b));
|
||||
};
|
||||
|
||||
// when a range contains different types, nothing should get returned
|
||||
for (const auto &value_a : values)
|
||||
for (const auto &value_b : values) {
|
||||
if (PropertyValue::AreComparableTypes(
|
||||
static_cast<PropertyValue>(value_a).type(),
|
||||
static_cast<PropertyValue>(value_b).type()))
|
||||
if (are_comparable(static_cast<PropertyValue>(value_a).type(),
|
||||
static_cast<PropertyValue>(value_b).type()))
|
||||
continue;
|
||||
check(TypedValue(value_a), Bound::Type::INCLUSIVE, TypedValue(value_b),
|
||||
Bound::Type::INCLUSIVE, {});
|
||||
|
Loading…
Reference in New Issue
Block a user