Add TemporalData to PropertyValue (#174)
This commit is contained in:
parent
ccca46370d
commit
5da32c1bff
@ -5,6 +5,7 @@
|
||||
#include <fmt/format.h>
|
||||
#include <json/json.hpp>
|
||||
|
||||
#include "storage/v2/temporal.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
#include "utils/string.hpp"
|
||||
|
||||
@ -42,6 +43,14 @@ inline nlohmann::json PropertyValueToJson(const storage::PropertyValue &pv) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case storage::PropertyValue::Type::TemporalData: {
|
||||
ret = nlohmann::json::object();
|
||||
const auto temporal_data = pv.ValueTemporalData();
|
||||
// TODO (antonio2368): Maybe we want to have custom format for each type
|
||||
ret.emplace("type", storage::TemporalTypeTostring(temporal_data.type));
|
||||
ret.emplace("microseconds", temporal_data.microseconds);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
@ -216,6 +216,8 @@ Value ToBoltValue(const storage::PropertyValue &value) {
|
||||
}
|
||||
return Value(std::move(dv_map));
|
||||
}
|
||||
case storage::PropertyValue::Type::TemporalData:
|
||||
LOG_FATAL("Unsupported type");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "query/exceptions.hpp"
|
||||
#include "query/stream.hpp"
|
||||
#include "query/typed_value.hpp"
|
||||
#include "storage/v2/property_value.hpp"
|
||||
#include "storage/v2/storage.hpp"
|
||||
#include "utils/algorithm.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
@ -88,6 +89,10 @@ void DumpPropertyValue(std::ostream *os, const storage::PropertyValue &value) {
|
||||
*os << "}";
|
||||
return;
|
||||
}
|
||||
case storage::PropertyValue::Type::TemporalData: {
|
||||
// TODO(antonio2368): Define dump command for temporal data
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -141,6 +141,9 @@ void PrintObject(std::ostream *out, const storage::PropertyValue &value) {
|
||||
case storage::PropertyValue::Type::Map:
|
||||
PrintObject(out, value.ValueMap());
|
||||
break;
|
||||
case storage::PropertyValue::Type::TemporalData:
|
||||
// TODO (antonio2368): Print out the temporal data based on the type
|
||||
LOG_FATAL("Not implemented!");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -491,6 +491,7 @@ UniqueCursorPtr ScanAllByLabelPropertyRange::MakeCursor(utils::MemoryResource *m
|
||||
case storage::PropertyValue::Type::Int:
|
||||
case storage::PropertyValue::Type::Double:
|
||||
case storage::PropertyValue::Type::String:
|
||||
case storage::PropertyValue::Type::TemporalData:
|
||||
// These are all fine, there's also Point, Date and Time data types
|
||||
// which were added to Cypher, but we don't have support for those
|
||||
// yet.
|
||||
|
@ -477,6 +477,10 @@ mgp_value::mgp_value(const storage::PropertyValue &pv, utils::MemoryResource *m)
|
||||
map_v = allocator.new_object<mgp_map>(std::move(items));
|
||||
break;
|
||||
}
|
||||
case storage::PropertyValue::Type::TemporalData: {
|
||||
// TODO (antonio2368): Add support for temporala data types
|
||||
LOG_FATAL("Unsupported type");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,10 @@
|
||||
|
||||
namespace query::serialization {
|
||||
|
||||
namespace {
|
||||
enum class ObjectType : uint8_t { MAP, TEMPORAL_DATA };
|
||||
} // namespace
|
||||
|
||||
nlohmann::json SerializePropertyValue(const storage::PropertyValue &property_value) {
|
||||
using Type = storage::PropertyValue::Type;
|
||||
switch (property_value.type()) {
|
||||
@ -21,6 +25,13 @@ nlohmann::json SerializePropertyValue(const storage::PropertyValue &property_val
|
||||
return SerializePropertyValueVector(property_value.ValueList());
|
||||
case Type::Map:
|
||||
return SerializePropertyValueMap(property_value.ValueMap());
|
||||
case Type::TemporalData:
|
||||
const auto temporal_data = property_value.ValueTemporalData();
|
||||
auto data = nlohmann::json::object();
|
||||
data.emplace("type", static_cast<uint64_t>(ObjectType::TEMPORAL_DATA));
|
||||
data.emplace("value", nlohmann::json::object({{"type", static_cast<uint64_t>(temporal_data.type)},
|
||||
{"microseconds", temporal_data.microseconds}}));
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,9 +45,11 @@ nlohmann::json SerializePropertyValueVector(const std::vector<storage::PropertyV
|
||||
|
||||
nlohmann::json SerializePropertyValueMap(const std::map<std::string, storage::PropertyValue> ¶meters) {
|
||||
nlohmann::json data = nlohmann::json::object();
|
||||
data.emplace("type", static_cast<uint64_t>(ObjectType::MAP));
|
||||
data.emplace("value", nlohmann::json::object());
|
||||
|
||||
for (const auto &[key, value] : parameters) {
|
||||
data[key] = SerializePropertyValue(value);
|
||||
data["value"][key] = SerializePropertyValue(value);
|
||||
}
|
||||
|
||||
return data;
|
||||
@ -68,7 +81,14 @@ storage::PropertyValue DeserializePropertyValue(const nlohmann::json &data) {
|
||||
}
|
||||
|
||||
MG_ASSERT(data.is_object(), "Unknown type found in the trigger storage");
|
||||
return storage::PropertyValue(DeserializePropertyValueMap(data));
|
||||
|
||||
switch (data["type"].get<ObjectType>()) {
|
||||
case ObjectType::MAP:
|
||||
return storage::PropertyValue(DeserializePropertyValueMap(data));
|
||||
case ObjectType::TEMPORAL_DATA:
|
||||
return storage::PropertyValue(storage::TemporalData{data["value"]["type"].get<storage::TemporalType>(),
|
||||
data["value"]["microseconds"].get<int64_t>()});
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<storage::PropertyValue> DeserializePropertyValueList(const nlohmann::json::array_t &data) {
|
||||
@ -82,9 +102,11 @@ std::vector<storage::PropertyValue> DeserializePropertyValueList(const nlohmann:
|
||||
}
|
||||
|
||||
std::map<std::string, storage::PropertyValue> DeserializePropertyValueMap(const nlohmann::json::object_t &data) {
|
||||
MG_ASSERT(data.at("type").get<ObjectType>() == ObjectType::MAP, "Invalid map serialization");
|
||||
std::map<std::string, storage::PropertyValue> property_values;
|
||||
|
||||
for (const auto &[key, value] : data) {
|
||||
const nlohmann::json::object_t &values = data.at("value");
|
||||
for (const auto &[key, value] : values) {
|
||||
property_values.emplace(key, DeserializePropertyValue(value));
|
||||
}
|
||||
|
||||
|
@ -52,6 +52,9 @@ TypedValue::TypedValue(const storage::PropertyValue &value, utils::MemoryResourc
|
||||
for (const auto &kv : map) map_v.emplace(kv.first, kv.second);
|
||||
return;
|
||||
}
|
||||
case storage::PropertyValue::Type::TemporalData:
|
||||
// TODO (antonio2368): Add support for Temporal types in TypedValues
|
||||
break;
|
||||
}
|
||||
LOG_FATAL("Unsupported type");
|
||||
}
|
||||
@ -96,6 +99,9 @@ TypedValue::TypedValue(storage::PropertyValue &&other, utils::MemoryResource *me
|
||||
for (auto &kv : map) map_v.emplace(kv.first, std::move(kv.second));
|
||||
break;
|
||||
}
|
||||
case storage::PropertyValue::Type::TemporalData:
|
||||
// TODO (antonio2368): Add support for Temporal types in TypedValues
|
||||
LOG_FATAL("Unsupported type");
|
||||
}
|
||||
|
||||
other = storage::PropertyValue();
|
||||
|
@ -1,6 +1,7 @@
|
||||
set(storage_v2_src_files
|
||||
commit_log.cpp
|
||||
constraints.cpp
|
||||
temporal.cpp
|
||||
durability/durability.cpp
|
||||
durability/serialization.cpp
|
||||
durability/snapshot.cpp
|
||||
|
@ -16,6 +16,7 @@ enum class Marker : uint8_t {
|
||||
TYPE_LIST = 0x15,
|
||||
TYPE_MAP = 0x16,
|
||||
TYPE_PROPERTY_VALUE = 0x17,
|
||||
TYPE_TEMPORAL_DATA = 0x18,
|
||||
|
||||
SECTION_VERTEX = 0x20,
|
||||
SECTION_EDGE = 0x21,
|
||||
@ -59,6 +60,7 @@ static const Marker kMarkersAll[] = {
|
||||
Marker::TYPE_STRING,
|
||||
Marker::TYPE_LIST,
|
||||
Marker::TYPE_MAP,
|
||||
Marker::TYPE_TEMPORAL_DATA,
|
||||
Marker::TYPE_PROPERTY_VALUE,
|
||||
Marker::SECTION_VERTEX,
|
||||
Marker::SECTION_EDGE,
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "storage/v2/durability/serialization.hpp"
|
||||
|
||||
#include "storage/v2/temporal.hpp"
|
||||
#include "utils/endian.hpp"
|
||||
|
||||
namespace storage::durability {
|
||||
@ -109,6 +110,13 @@ void Encoder::WritePropertyValue(const PropertyValue &value) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PropertyValue::Type::TemporalData: {
|
||||
const auto temporal_data = value.ValueTemporalData();
|
||||
WriteMarker(Marker::TYPE_TEMPORAL_DATA);
|
||||
WriteUint(static_cast<uint64_t>(temporal_data.type));
|
||||
WriteUint(utils::MemcpyCast<uint64_t>(temporal_data.microseconds));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -222,6 +230,21 @@ std::optional<std::string> Decoder::ReadString() {
|
||||
return value;
|
||||
}
|
||||
|
||||
namespace {
|
||||
std::optional<TemporalData> ReadTemporalData(Decoder &decoder) {
|
||||
const auto inner_marker = decoder.ReadMarker();
|
||||
if (!inner_marker || *inner_marker != Marker::TYPE_TEMPORAL_DATA) return std::nullopt;
|
||||
|
||||
const auto type = decoder.ReadUint();
|
||||
if (!type) return std::nullopt;
|
||||
|
||||
const auto microseconds = decoder.ReadUint();
|
||||
if (!microseconds) return std::nullopt;
|
||||
|
||||
return TemporalData{static_cast<TemporalType>(*type), utils::MemcpyCast<int64_t>(*microseconds)};
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::optional<PropertyValue> Decoder::ReadPropertyValue() {
|
||||
auto pv_marker = ReadMarker();
|
||||
if (!pv_marker || *pv_marker != Marker::TYPE_PROPERTY_VALUE) return std::nullopt;
|
||||
@ -283,6 +306,11 @@ std::optional<PropertyValue> Decoder::ReadPropertyValue() {
|
||||
}
|
||||
return PropertyValue(std::move(value));
|
||||
}
|
||||
case Marker::TYPE_TEMPORAL_DATA: {
|
||||
const auto maybe_temporal_data = ReadTemporalData(*this);
|
||||
if (!maybe_temporal_data) return std::nullopt;
|
||||
return PropertyValue(*maybe_temporal_data);
|
||||
}
|
||||
|
||||
case Marker::TYPE_PROPERTY_VALUE:
|
||||
case Marker::SECTION_VERTEX:
|
||||
@ -379,6 +407,9 @@ bool Decoder::SkipPropertyValue() {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case Marker::TYPE_TEMPORAL_DATA: {
|
||||
return !!ReadTemporalData(*this);
|
||||
}
|
||||
|
||||
case Marker::TYPE_PROPERTY_VALUE:
|
||||
case Marker::SECTION_VERTEX:
|
||||
|
@ -158,6 +158,7 @@ WalDeltaData::Type MarkerToWalDeltaDataType(Marker marker) {
|
||||
case Marker::TYPE_STRING:
|
||||
case Marker::TYPE_LIST:
|
||||
case Marker::TYPE_MAP:
|
||||
case Marker::TYPE_TEMPORAL_DATA:
|
||||
case Marker::TYPE_PROPERTY_VALUE:
|
||||
case Marker::SECTION_VERTEX:
|
||||
case Marker::SECTION_EDGE:
|
||||
|
@ -1,6 +1,8 @@
|
||||
#include "indices.hpp"
|
||||
|
||||
#include "storage/v2/mvcc.hpp"
|
||||
#include "storage/v2/property_value.hpp"
|
||||
#include "utils/bound.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
#include "utils/memory_tracker.hpp"
|
||||
|
||||
@ -519,6 +521,7 @@ const PropertyValue kSmallestNumber = PropertyValue(-std::numeric_limits<double>
|
||||
const PropertyValue kSmallestString = PropertyValue("");
|
||||
const PropertyValue kSmallestList = PropertyValue(std::vector<PropertyValue>());
|
||||
const PropertyValue kSmallestMap = PropertyValue(std::map<std::string, PropertyValue>());
|
||||
const PropertyValue kSmallestTemporalData = PropertyValue(TemporalData{static_cast<TemporalType>(0), 0});
|
||||
|
||||
LabelPropertyIndex::Iterable::Iterable(utils::SkipList<Entry>::Accessor index_accessor, LabelId label,
|
||||
PropertyId property,
|
||||
@ -590,6 +593,9 @@ LabelPropertyIndex::Iterable::Iterable(utils::SkipList<Entry>::Accessor index_ac
|
||||
upper_bound_ = utils::MakeBoundExclusive(kSmallestMap);
|
||||
break;
|
||||
case PropertyValue::Type::Map:
|
||||
upper_bound_ = utils::MakeBoundExclusive(kSmallestTemporalData);
|
||||
break;
|
||||
case PropertyValue::Type::TemporalData:
|
||||
// This is the last type in the order so we leave the upper bound empty.
|
||||
break;
|
||||
}
|
||||
@ -619,7 +625,9 @@ LabelPropertyIndex::Iterable::Iterable(utils::SkipList<Entry>::Accessor index_ac
|
||||
break;
|
||||
case PropertyValue::Type::Map:
|
||||
lower_bound_ = utils::MakeBoundInclusive(kSmallestMap);
|
||||
// This is the last type in the order so we leave the upper bound empty.
|
||||
break;
|
||||
case PropertyValue::Type::TemporalData:
|
||||
lower_bound_ = utils::MakeBoundInclusive(kSmallestTemporalData);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "storage/v2/temporal.hpp"
|
||||
#include "utils/cast.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
|
||||
@ -90,6 +91,7 @@ enum class Type : uint8_t {
|
||||
STRING = 0x50,
|
||||
LIST = 0x60,
|
||||
MAP = 0x70,
|
||||
TEMPORAL_DATA = 0x80
|
||||
};
|
||||
|
||||
const uint8_t kMaskType = 0xf0;
|
||||
@ -141,6 +143,16 @@ const uint8_t kShiftIdSize = 2;
|
||||
// + encoded key data
|
||||
// + encoded value size
|
||||
// + encoded value data
|
||||
// * TEMPORAL_DATE
|
||||
// - type; payload size isn't used
|
||||
// - encoded property ID
|
||||
// - value saved as Metadata
|
||||
// + type; id size is used to indicate whether the temporal data type is encoded
|
||||
// as `uint8_t`, `uint16_t`, `uint32_t` or `uint64_t`; payload size used to
|
||||
// indicate whether the microseconds are encoded as `uint8_t`, `uint16_t, `uint32_t
|
||||
// or `uint64_t`
|
||||
// + encoded temporal data type value
|
||||
// + encoded microseconds value
|
||||
|
||||
struct Metadata {
|
||||
Type type{Type::EMPTY};
|
||||
@ -428,9 +440,40 @@ std::optional<std::pair<Type, Size>> EncodePropertyValue(Writer *writer, const P
|
||||
}
|
||||
return {{Type::MAP, *size}};
|
||||
}
|
||||
case PropertyValue::Type::TemporalData: {
|
||||
auto metadata = writer->WriteMetadata();
|
||||
if (!metadata) return std::nullopt;
|
||||
|
||||
const auto temporal_data = value.ValueTemporalData();
|
||||
auto type_size = writer->WriteUint(utils::UnderlyingCast(temporal_data.type));
|
||||
if (!type_size) return std::nullopt;
|
||||
|
||||
auto microseconds_size = writer->WriteInt(temporal_data.microseconds);
|
||||
if (!microseconds_size) return std::nullopt;
|
||||
metadata->Set({Type::TEMPORAL_DATA, *type_size, *microseconds_size});
|
||||
|
||||
// We don't need payload size so we set it to a random value
|
||||
return {{Type::TEMPORAL_DATA, Size::INT8}};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
std::optional<TemporalData> DecodeTemporalData(Reader &reader) {
|
||||
auto metadata = reader.ReadMetadata();
|
||||
if (!metadata || metadata->type != Type::TEMPORAL_DATA) return std::nullopt;
|
||||
|
||||
auto type_value = reader.ReadUint(metadata->id_size);
|
||||
if (!type_value) return std::nullopt;
|
||||
|
||||
auto microseconds_value = reader.ReadInt(metadata->payload_size);
|
||||
if (!microseconds_value) return std::nullopt;
|
||||
|
||||
return TemporalData{static_cast<TemporalType>(*type_value), *microseconds_value};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// Function used to decode a PropertyValue from a byte stream. It can either
|
||||
// decode or skip the encoded PropertyValue, depending on the supplied value
|
||||
// pointer.
|
||||
@ -537,6 +580,18 @@ std::optional<std::pair<Type, Size>> EncodePropertyValue(Writer *writer, const P
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
case Type::TEMPORAL_DATA: {
|
||||
const auto maybe_temporal_data = DecodeTemporalData(*reader);
|
||||
|
||||
if (!maybe_temporal_data) return false;
|
||||
|
||||
if (value) {
|
||||
*value = PropertyValue(*maybe_temporal_data);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -627,6 +682,16 @@ std::optional<std::pair<Type, Size>> EncodePropertyValue(Writer *writer, const P
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case Type::TEMPORAL_DATA: {
|
||||
if (!value.IsTemporalData()) return false;
|
||||
|
||||
const auto maybe_temporal_data = DecodeTemporalData(*reader);
|
||||
if (!maybe_temporal_data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return *maybe_temporal_data == value.ValueTemporalData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "storage/v2/temporal.hpp"
|
||||
#include "utils/algorithm.hpp"
|
||||
#include "utils/exceptions.hpp"
|
||||
|
||||
@ -33,6 +34,7 @@ class PropertyValue {
|
||||
String = 4,
|
||||
List = 5,
|
||||
Map = 6,
|
||||
TemporalData = 7
|
||||
};
|
||||
|
||||
static bool AreComparableTypes(Type a, Type b) {
|
||||
@ -43,10 +45,11 @@ class PropertyValue {
|
||||
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; }
|
||||
explicit PropertyValue(const bool value) : type_(Type::Bool) { bool_v = value; }
|
||||
explicit PropertyValue(const int value) : type_(Type::Int) { int_v = value; }
|
||||
explicit PropertyValue(const int64_t value) : type_(Type::Int) { int_v = value; }
|
||||
explicit PropertyValue(const double value) : type_(Type::Double) { double_v = value; }
|
||||
explicit PropertyValue(const TemporalData value) : type_{Type::TemporalData} { temporal_data_v = value; }
|
||||
|
||||
// copy constructors for non-primitive types
|
||||
/// @throw std::bad_alloc
|
||||
@ -103,6 +106,7 @@ class PropertyValue {
|
||||
bool IsString() const { return type_ == Type::String; }
|
||||
bool IsList() const { return type_ == Type::List; }
|
||||
bool IsMap() const { return type_ == Type::Map; }
|
||||
bool IsTemporalData() const { return type_ == Type::TemporalData; }
|
||||
|
||||
// value getters for primitive types
|
||||
/// @throw PropertyValueException if value isn't of correct type.
|
||||
@ -127,6 +131,15 @@ class PropertyValue {
|
||||
return double_v;
|
||||
}
|
||||
|
||||
/// @throw PropertyValueException if value isn't of correct type.
|
||||
TemporalData ValueTemporalData() const {
|
||||
if (type_ != Type::TemporalData) {
|
||||
throw PropertyValueException("The value isn't a temporal data!");
|
||||
}
|
||||
|
||||
return temporal_data_v;
|
||||
}
|
||||
|
||||
// const value getters for non-primitive types
|
||||
/// @throw PropertyValueException if value isn't of correct type.
|
||||
const std::string &ValueString() const {
|
||||
@ -187,6 +200,7 @@ class PropertyValue {
|
||||
std::string string_v;
|
||||
std::vector<PropertyValue> list_v;
|
||||
std::map<std::string, PropertyValue> map_v;
|
||||
TemporalData temporal_data_v;
|
||||
};
|
||||
|
||||
Type type_;
|
||||
@ -210,6 +224,8 @@ inline std::ostream &operator<<(std::ostream &os, const PropertyValue::Type type
|
||||
return os << "list";
|
||||
case PropertyValue::Type::Map:
|
||||
return os << "map";
|
||||
case PropertyValue::Type::TemporalData:
|
||||
return os << "temporal data";
|
||||
}
|
||||
}
|
||||
/// @throw anything std::ostream::operator<< may throw.
|
||||
@ -234,6 +250,9 @@ inline std::ostream &operator<<(std::ostream &os, const PropertyValue &value) {
|
||||
utils::PrintIterable(os, value.ValueMap(), ", ",
|
||||
[](auto &stream, const auto &pair) { stream << pair.first << ": " << pair.second; });
|
||||
return os << "}";
|
||||
case PropertyValue::Type::TemporalData:
|
||||
return os << fmt::format("type: {}, microseconds: {}", TemporalTypeTostring(value.ValueTemporalData().type),
|
||||
value.ValueTemporalData().microseconds);
|
||||
}
|
||||
}
|
||||
|
||||
@ -265,6 +284,8 @@ inline bool operator==(const PropertyValue &first, const PropertyValue &second)
|
||||
return first.ValueList() == second.ValueList();
|
||||
case PropertyValue::Type::Map:
|
||||
return first.ValueMap() == second.ValueMap();
|
||||
case PropertyValue::Type::TemporalData:
|
||||
return first.ValueTemporalData() == second.ValueTemporalData();
|
||||
}
|
||||
}
|
||||
|
||||
@ -293,6 +314,8 @@ inline bool operator<(const PropertyValue &first, const PropertyValue &second) n
|
||||
return first.ValueList() < second.ValueList();
|
||||
case PropertyValue::Type::Map:
|
||||
return first.ValueMap() < second.ValueMap();
|
||||
case PropertyValue::Type::TemporalData:
|
||||
return first.ValueTemporalData() < second.ValueTemporalData();
|
||||
}
|
||||
}
|
||||
|
||||
@ -318,6 +341,9 @@ inline PropertyValue::PropertyValue(const PropertyValue &other) : type_(other.ty
|
||||
case Type::Map:
|
||||
new (&map_v) std::map<std::string, PropertyValue>(other.map_v);
|
||||
return;
|
||||
case Type::TemporalData:
|
||||
this->temporal_data_v = other.temporal_data_v;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@ -343,6 +369,9 @@ inline PropertyValue::PropertyValue(PropertyValue &&other) noexcept : type_(othe
|
||||
case Type::Map:
|
||||
new (&map_v) std::map<std::string, PropertyValue>(std::move(other.map_v));
|
||||
break;
|
||||
case Type::TemporalData:
|
||||
this->temporal_data_v = other.temporal_data_v;
|
||||
break;
|
||||
}
|
||||
|
||||
// reset the type of other
|
||||
@ -377,6 +406,9 @@ inline PropertyValue &PropertyValue::operator=(const PropertyValue &other) {
|
||||
case Type::Map:
|
||||
new (&map_v) std::map<std::string, PropertyValue>(other.map_v);
|
||||
break;
|
||||
case Type::TemporalData:
|
||||
this->temporal_data_v = other.temporal_data_v;
|
||||
break;
|
||||
}
|
||||
|
||||
return *this;
|
||||
@ -409,6 +441,9 @@ inline PropertyValue &PropertyValue::operator=(PropertyValue &&other) noexcept {
|
||||
case Type::Map:
|
||||
new (&map_v) std::map<std::string, PropertyValue>(std::move(other.map_v));
|
||||
break;
|
||||
case Type::TemporalData:
|
||||
this->temporal_data_v = other.temporal_data_v;
|
||||
break;
|
||||
}
|
||||
|
||||
// reset the type of other
|
||||
@ -425,20 +460,18 @@ inline void PropertyValue::DestroyValue() noexcept {
|
||||
case Type::Bool:
|
||||
case Type::Int:
|
||||
case Type::Double:
|
||||
case Type::TemporalData:
|
||||
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();
|
||||
std::destroy_at(&string_v);
|
||||
return;
|
||||
case Type::List:
|
||||
list_v.~vector();
|
||||
std::destroy_at(&list_v);
|
||||
return;
|
||||
case Type::Map:
|
||||
map_v.~map();
|
||||
std::destroy_at(&map_v);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "storage/v2/property_value.hpp"
|
||||
#include "storage/v2/temporal.hpp"
|
||||
#include "utils/cast.hpp"
|
||||
|
||||
namespace slk {
|
||||
@ -14,10 +16,6 @@ void Load(storage::Gid *gid, slk::Reader *reader) {
|
||||
*gid = storage::Gid::FromUint(value);
|
||||
}
|
||||
|
||||
void Save(const storage::PropertyValue::Type &type, slk::Builder *builder) {
|
||||
slk::Save(utils::UnderlyingCast(type), builder);
|
||||
}
|
||||
|
||||
void Load(storage::PropertyValue::Type *type, slk::Reader *reader) {
|
||||
using PVTypeUnderlyingType = std::underlying_type_t<storage::PropertyValue::Type>;
|
||||
PVTypeUnderlyingType value;
|
||||
@ -31,6 +29,7 @@ void Load(storage::PropertyValue::Type *type, slk::Reader *reader) {
|
||||
case utils::UnderlyingCast(storage::PropertyValue::Type::String):
|
||||
case utils::UnderlyingCast(storage::PropertyValue::Type::List):
|
||||
case utils::UnderlyingCast(storage::PropertyValue::Type::Map):
|
||||
case utils::UnderlyingCast(storage::PropertyValue::Type::TemporalData):
|
||||
valid = true;
|
||||
break;
|
||||
default:
|
||||
@ -82,6 +81,13 @@ void Save(const storage::PropertyValue &value, slk::Builder *builder) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
case storage::PropertyValue::Type::TemporalData: {
|
||||
slk::Save(storage::PropertyValue::Type::TemporalData, builder);
|
||||
const auto temporal_data = value.ValueTemporalData();
|
||||
slk::Save(temporal_data.type, builder);
|
||||
slk::Save(temporal_data.microseconds, builder);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -138,18 +144,15 @@ void Load(storage::PropertyValue *value, slk::Reader *reader) {
|
||||
*value = storage::PropertyValue(std::move(map));
|
||||
return;
|
||||
}
|
||||
case storage::PropertyValue::Type::TemporalData: {
|
||||
storage::TemporalType temporal_type;
|
||||
slk::Load(&temporal_type, reader);
|
||||
int64_t microseconds{0};
|
||||
slk::Load(µseconds, reader);
|
||||
*value = storage::PropertyValue(storage::TemporalData{temporal_type, microseconds});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Save(const storage::durability::Marker &marker, slk::Builder *builder) {
|
||||
slk::Save(utils::UnderlyingCast(marker), builder);
|
||||
}
|
||||
|
||||
void Load(storage::durability::Marker *marker, slk::Reader *reader) {
|
||||
using PVTypeUnderlyingType = std::underlying_type_t<storage::PropertyValue::Type>;
|
||||
PVTypeUnderlyingType value;
|
||||
slk::Load(&value, reader);
|
||||
*marker = static_cast<storage::durability::Marker>(value);
|
||||
}
|
||||
|
||||
} // namespace slk
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "storage/v2/durability/marker.hpp"
|
||||
#include "storage/v2/id_types.hpp"
|
||||
#include "storage/v2/property_value.hpp"
|
||||
#include "utils/concepts.hpp"
|
||||
|
||||
namespace slk {
|
||||
|
||||
@ -13,7 +14,17 @@ void Load(storage::Gid *gid, slk::Reader *reader);
|
||||
void Save(const storage::PropertyValue &value, slk::Builder *builder);
|
||||
void Load(storage::PropertyValue *value, slk::Reader *reader);
|
||||
|
||||
void Save(const storage::durability::Marker &marker, slk::Builder *builder);
|
||||
void Load(storage::durability::Marker *marker, slk::Reader *reader);
|
||||
template <utils::Enum T>
|
||||
void Save(const T &enum_value, slk::Builder *builder) {
|
||||
slk::Save(utils::UnderlyingCast(enum_value), builder);
|
||||
}
|
||||
|
||||
template <utils::Enum T>
|
||||
void Load(T *enum_value, slk::Reader *reader) {
|
||||
using UnderlyingType = std::underlying_type_t<T>;
|
||||
UnderlyingType value;
|
||||
slk::Load(&value, reader);
|
||||
*enum_value = static_cast<T>(value);
|
||||
}
|
||||
|
||||
} // namespace slk
|
||||
|
6
src/storage/v2/temporal.cpp
Normal file
6
src/storage/v2/temporal.cpp
Normal file
@ -0,0 +1,6 @@
|
||||
#include "storage/v2/temporal.hpp"
|
||||
|
||||
namespace storage {
|
||||
TemporalData::TemporalData(TemporalType type, int64_t microseconds) : type{type}, microseconds{microseconds} {}
|
||||
|
||||
} // namespace storage
|
31
src/storage/v2/temporal.hpp
Normal file
31
src/storage/v2/temporal.hpp
Normal file
@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <string_view>
|
||||
|
||||
namespace storage {
|
||||
|
||||
enum class TemporalType : uint8_t { Date = 0, LocalTime, LocalDateTime, Duration };
|
||||
|
||||
constexpr std::string_view TemporalTypeTostring(const TemporalType type) {
|
||||
switch (type) {
|
||||
case TemporalType::Date:
|
||||
return "Date";
|
||||
case TemporalType::LocalTime:
|
||||
return "LocalTime";
|
||||
case TemporalType::LocalDateTime:
|
||||
return "LocalDateTime";
|
||||
case TemporalType::Duration:
|
||||
return "Duration";
|
||||
}
|
||||
}
|
||||
|
||||
struct TemporalData {
|
||||
explicit TemporalData(TemporalType type, int64_t microseconds);
|
||||
|
||||
auto operator<=>(const TemporalData &) const = default;
|
||||
|
||||
TemporalType type;
|
||||
int64_t microseconds;
|
||||
};
|
||||
|
||||
} // namespace storage
|
@ -4,4 +4,7 @@
|
||||
namespace utils {
|
||||
template <typename T, typename... Args>
|
||||
concept SameAsAnyOf = (std::same_as<T, Args> || ...);
|
||||
|
||||
template <typename T>
|
||||
concept Enum = std::is_enum_v<T>;
|
||||
} // namespace utils
|
||||
|
@ -282,7 +282,7 @@ add_unit_test(commit_log_v2.cpp)
|
||||
target_link_libraries(${test_prefix}commit_log_v2 gflags mg-utils mg-storage-v2)
|
||||
|
||||
add_unit_test(property_value_v2.cpp)
|
||||
target_link_libraries(${test_prefix}property_value_v2 mg-utils)
|
||||
target_link_libraries(${test_prefix}property_value_v2 mg-storage-v2 mg-utils)
|
||||
|
||||
add_unit_test(storage_v2.cpp)
|
||||
target_link_libraries(${test_prefix}storage_v2 mg-storage-v2 storage_test_utils)
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <sstream>
|
||||
|
||||
#include "storage/v2/property_value.hpp"
|
||||
#include "storage/v2/temporal.hpp"
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST(PropertyValue, Null) {
|
||||
@ -496,10 +497,16 @@ TEST(PropertyValue, MapMove) {
|
||||
TEST(PropertyValue, CopyConstructor) {
|
||||
std::vector<storage::PropertyValue> vec{storage::PropertyValue(true), storage::PropertyValue(123)};
|
||||
std::map<std::string, storage::PropertyValue> map{{"nandare", storage::PropertyValue(false)}};
|
||||
std::vector<storage::PropertyValue> data{storage::PropertyValue(), storage::PropertyValue(true),
|
||||
storage::PropertyValue(123), storage::PropertyValue(123.5),
|
||||
storage::PropertyValue("nandare"), storage::PropertyValue(vec),
|
||||
storage::PropertyValue(map)};
|
||||
std::vector<storage::PropertyValue> data{
|
||||
storage::PropertyValue(),
|
||||
storage::PropertyValue(true),
|
||||
storage::PropertyValue(123),
|
||||
storage::PropertyValue(123.5),
|
||||
storage::PropertyValue("nandare"),
|
||||
storage::PropertyValue(vec),
|
||||
storage::PropertyValue(map),
|
||||
storage::PropertyValue(storage::TemporalData(storage::TemporalType::Date, 23))};
|
||||
|
||||
for (const auto &item : data) {
|
||||
storage::PropertyValue pv(item);
|
||||
ASSERT_EQ(pv.type(), item.type());
|
||||
@ -525,6 +532,8 @@ TEST(PropertyValue, CopyConstructor) {
|
||||
case storage::PropertyValue::Type::Map:
|
||||
ASSERT_EQ(pv.ValueMap(), item.ValueMap());
|
||||
break;
|
||||
case storage::PropertyValue::Type::TemporalData:
|
||||
ASSERT_EQ(pv.ValueTemporalData(), item.ValueTemporalData());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -533,10 +542,16 @@ TEST(PropertyValue, CopyConstructor) {
|
||||
TEST(PropertyValue, MoveConstructor) {
|
||||
std::vector<storage::PropertyValue> vec{storage::PropertyValue(true), storage::PropertyValue(123)};
|
||||
std::map<std::string, storage::PropertyValue> map{{"nandare", storage::PropertyValue(false)}};
|
||||
std::vector<storage::PropertyValue> data{storage::PropertyValue(), storage::PropertyValue(true),
|
||||
storage::PropertyValue(123), storage::PropertyValue(123.5),
|
||||
storage::PropertyValue("nandare"), storage::PropertyValue(vec),
|
||||
storage::PropertyValue(map)};
|
||||
std::vector<storage::PropertyValue> data{
|
||||
storage::PropertyValue(),
|
||||
storage::PropertyValue(true),
|
||||
storage::PropertyValue(123),
|
||||
storage::PropertyValue(123.5),
|
||||
storage::PropertyValue("nandare"),
|
||||
storage::PropertyValue(vec),
|
||||
storage::PropertyValue(map),
|
||||
storage::PropertyValue(storage::TemporalData(storage::TemporalType::Date, 23))};
|
||||
|
||||
for (auto &item : data) {
|
||||
storage::PropertyValue copy(item);
|
||||
storage::PropertyValue pv(std::move(item));
|
||||
@ -564,6 +579,9 @@ TEST(PropertyValue, MoveConstructor) {
|
||||
case storage::PropertyValue::Type::Map:
|
||||
ASSERT_EQ(pv.ValueMap(), copy.ValueMap());
|
||||
break;
|
||||
case storage::PropertyValue::Type::TemporalData:
|
||||
ASSERT_EQ(pv.ValueTemporalData(), copy.ValueTemporalData());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -572,10 +590,16 @@ TEST(PropertyValue, MoveConstructor) {
|
||||
TEST(PropertyValue, CopyAssignment) {
|
||||
std::vector<storage::PropertyValue> vec{storage::PropertyValue(true), storage::PropertyValue(123)};
|
||||
std::map<std::string, storage::PropertyValue> map{{"nandare", storage::PropertyValue(false)}};
|
||||
std::vector<storage::PropertyValue> data{storage::PropertyValue(), storage::PropertyValue(true),
|
||||
storage::PropertyValue(123), storage::PropertyValue(123.5),
|
||||
storage::PropertyValue("nandare"), storage::PropertyValue(vec),
|
||||
storage::PropertyValue(map)};
|
||||
std::vector<storage::PropertyValue> data{
|
||||
storage::PropertyValue(),
|
||||
storage::PropertyValue(true),
|
||||
storage::PropertyValue(123),
|
||||
storage::PropertyValue(123.5),
|
||||
storage::PropertyValue("nandare"),
|
||||
storage::PropertyValue(vec),
|
||||
storage::PropertyValue(map),
|
||||
storage::PropertyValue(storage::TemporalData(storage::TemporalType::Date, 23))};
|
||||
|
||||
for (const auto &item : data) {
|
||||
storage::PropertyValue pv(123);
|
||||
pv = item;
|
||||
@ -602,6 +626,9 @@ TEST(PropertyValue, CopyAssignment) {
|
||||
case storage::PropertyValue::Type::Map:
|
||||
ASSERT_EQ(pv.ValueMap(), item.ValueMap());
|
||||
break;
|
||||
case storage::PropertyValue::Type::TemporalData:
|
||||
ASSERT_EQ(pv.ValueTemporalData(), item.ValueTemporalData());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -610,10 +637,16 @@ TEST(PropertyValue, CopyAssignment) {
|
||||
TEST(PropertyValue, MoveAssignment) {
|
||||
std::vector<storage::PropertyValue> vec{storage::PropertyValue(true), storage::PropertyValue(123)};
|
||||
std::map<std::string, storage::PropertyValue> map{{"nandare", storage::PropertyValue(false)}};
|
||||
std::vector<storage::PropertyValue> data{storage::PropertyValue(), storage::PropertyValue(true),
|
||||
storage::PropertyValue(123), storage::PropertyValue(123.5),
|
||||
storage::PropertyValue("nandare"), storage::PropertyValue(vec),
|
||||
storage::PropertyValue(map)};
|
||||
std::vector<storage::PropertyValue> data{
|
||||
storage::PropertyValue(),
|
||||
storage::PropertyValue(true),
|
||||
storage::PropertyValue(123),
|
||||
storage::PropertyValue(123.5),
|
||||
storage::PropertyValue("nandare"),
|
||||
storage::PropertyValue(vec),
|
||||
storage::PropertyValue(map),
|
||||
storage::PropertyValue(storage::TemporalData(storage::TemporalType::Date, 23))};
|
||||
|
||||
for (auto &item : data) {
|
||||
storage::PropertyValue copy(item);
|
||||
storage::PropertyValue pv(123);
|
||||
@ -642,6 +675,9 @@ TEST(PropertyValue, MoveAssignment) {
|
||||
case storage::PropertyValue::Type::Map:
|
||||
ASSERT_EQ(pv.ValueMap(), copy.ValueMap());
|
||||
break;
|
||||
case storage::PropertyValue::Type::TemporalData:
|
||||
ASSERT_EQ(pv.ValueTemporalData(), copy.ValueTemporalData());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "query/serialization/property_value.hpp"
|
||||
#include "storage/v2/temporal.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
|
||||
namespace {
|
||||
@ -39,6 +40,16 @@ TEST(PropertyValueSerializationTest, String) {
|
||||
CheckJsonConversion(storage::PropertyValue{""});
|
||||
}
|
||||
|
||||
TEST(PropertyValueSerializationTest, TemporalData) {
|
||||
const auto test_temporal_data_conversion = [](const auto type, const auto microseconds) {
|
||||
CheckJsonConversion(storage::PropertyValue{storage::TemporalData{type, microseconds}});
|
||||
};
|
||||
|
||||
test_temporal_data_conversion(storage::TemporalType::Date, 20);
|
||||
test_temporal_data_conversion(storage::TemporalType::LocalDateTime, -20);
|
||||
test_temporal_data_conversion(storage::TemporalType::Duration, 10000);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
std::vector<storage::PropertyValue> GetPropertyValueListWithBasicTypes() {
|
||||
@ -59,27 +70,39 @@ std::map<std::string, storage::PropertyValue> GetPropertyValueMapWithBasicTypes(
|
||||
TEST(PropertyValueSerializationTest, List) {
|
||||
storage::PropertyValue list = storage::PropertyValue{GetPropertyValueListWithBasicTypes()};
|
||||
|
||||
SPDLOG_DEBUG("Basic list");
|
||||
CheckJsonConversion(list);
|
||||
{
|
||||
SCOPED_TRACE("Basic list");
|
||||
CheckJsonConversion(list);
|
||||
}
|
||||
|
||||
SPDLOG_DEBUG("Nested list");
|
||||
CheckJsonConversion(storage::PropertyValue{std::vector<storage::PropertyValue>{list, list}});
|
||||
{
|
||||
SCOPED_TRACE("Nested list");
|
||||
CheckJsonConversion(storage::PropertyValue{std::vector<storage::PropertyValue>{list, list}});
|
||||
}
|
||||
|
||||
SPDLOG_DEBUG("List with map");
|
||||
list.ValueList().emplace_back(GetPropertyValueMapWithBasicTypes());
|
||||
CheckJsonConversion(list);
|
||||
{
|
||||
SCOPED_TRACE("List with map");
|
||||
list.ValueList().emplace_back(GetPropertyValueMapWithBasicTypes());
|
||||
CheckJsonConversion(list);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(PropertyValueSerializationTest, Map) {
|
||||
auto map = GetPropertyValueMapWithBasicTypes();
|
||||
SPDLOG_DEBUG("Basic map");
|
||||
CheckJsonConversion(storage::PropertyValue{map});
|
||||
{
|
||||
SCOPED_TRACE("Basic map");
|
||||
CheckJsonConversion(storage::PropertyValue{map});
|
||||
}
|
||||
|
||||
SPDLOG_DEBUG("Nested map");
|
||||
map.emplace("map", storage::PropertyValue{map});
|
||||
CheckJsonConversion(storage::PropertyValue{map});
|
||||
{
|
||||
SCOPED_TRACE("Nested map");
|
||||
map.emplace("map", storage::PropertyValue{map});
|
||||
CheckJsonConversion(storage::PropertyValue{map});
|
||||
}
|
||||
|
||||
SPDLOG_DEBUG("Map with list");
|
||||
map.emplace("list", storage::PropertyValue{GetPropertyValueListWithBasicTypes()});
|
||||
CheckJsonConversion(storage::PropertyValue{map});
|
||||
{
|
||||
SCOPED_TRACE("Map with list");
|
||||
map.emplace("list", storage::PropertyValue{GetPropertyValueListWithBasicTypes()});
|
||||
CheckJsonConversion(storage::PropertyValue{map});
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,25 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "storage/v2/property_value.hpp"
|
||||
#include "storage/v2/replication/slk.hpp"
|
||||
|
||||
#include "slk_common.hpp"
|
||||
#include "storage/v2/temporal.hpp"
|
||||
|
||||
TEST(SlkAdvanced, PropertyValueList) {
|
||||
std::vector<storage::PropertyValue> original{storage::PropertyValue("hello world!"), storage::PropertyValue(5),
|
||||
storage::PropertyValue(1.123423), storage::PropertyValue(true),
|
||||
storage::PropertyValue()};
|
||||
std::vector<storage::PropertyValue> original{
|
||||
storage::PropertyValue("hello world!"),
|
||||
storage::PropertyValue(5),
|
||||
storage::PropertyValue(1.123423),
|
||||
storage::PropertyValue(true),
|
||||
storage::PropertyValue(),
|
||||
storage::PropertyValue(storage::TemporalData(storage::TemporalType::Date, 23))};
|
||||
ASSERT_EQ(original[0].type(), storage::PropertyValue::Type::String);
|
||||
ASSERT_EQ(original[1].type(), storage::PropertyValue::Type::Int);
|
||||
ASSERT_EQ(original[2].type(), storage::PropertyValue::Type::Double);
|
||||
ASSERT_EQ(original[3].type(), storage::PropertyValue::Type::Bool);
|
||||
ASSERT_EQ(original[4].type(), storage::PropertyValue::Type::Null);
|
||||
ASSERT_EQ(original[5].type(), storage::PropertyValue::Type::TemporalData);
|
||||
|
||||
slk::Loopback loopback;
|
||||
auto builder = loopback.GetBuilder();
|
||||
@ -26,16 +33,19 @@ TEST(SlkAdvanced, PropertyValueList) {
|
||||
}
|
||||
|
||||
TEST(SlkAdvanced, PropertyValueMap) {
|
||||
std::map<std::string, storage::PropertyValue> original{{"hello", storage::PropertyValue("world")},
|
||||
{"number", storage::PropertyValue(5)},
|
||||
{"real", storage::PropertyValue(1.123423)},
|
||||
{"truth", storage::PropertyValue(true)},
|
||||
{"nothing", storage::PropertyValue()}};
|
||||
std::map<std::string, storage::PropertyValue> original{
|
||||
{"hello", storage::PropertyValue("world")},
|
||||
{"number", storage::PropertyValue(5)},
|
||||
{"real", storage::PropertyValue(1.123423)},
|
||||
{"truth", storage::PropertyValue(true)},
|
||||
{"nothing", storage::PropertyValue()},
|
||||
{"date", storage::PropertyValue(storage::TemporalData(storage::TemporalType::Date, 23))}};
|
||||
ASSERT_EQ(original["hello"].type(), storage::PropertyValue::Type::String);
|
||||
ASSERT_EQ(original["number"].type(), storage::PropertyValue::Type::Int);
|
||||
ASSERT_EQ(original["real"].type(), storage::PropertyValue::Type::Double);
|
||||
ASSERT_EQ(original["truth"].type(), storage::PropertyValue::Type::Bool);
|
||||
ASSERT_EQ(original["nothing"].type(), storage::PropertyValue::Type::Null);
|
||||
ASSERT_EQ(original["date"].type(), storage::PropertyValue::Type::TemporalData);
|
||||
|
||||
slk::Loopback loopback;
|
||||
auto builder = loopback.GetBuilder();
|
||||
@ -49,25 +59,33 @@ TEST(SlkAdvanced, PropertyValueMap) {
|
||||
}
|
||||
|
||||
TEST(SlkAdvanced, PropertyValueComplex) {
|
||||
std::vector<storage::PropertyValue> vec_v{storage::PropertyValue("hello world!"), storage::PropertyValue(5),
|
||||
storage::PropertyValue(1.123423), storage::PropertyValue(true),
|
||||
storage::PropertyValue()};
|
||||
std::vector<storage::PropertyValue> vec_v{
|
||||
storage::PropertyValue("hello world!"),
|
||||
storage::PropertyValue(5),
|
||||
storage::PropertyValue(1.123423),
|
||||
storage::PropertyValue(true),
|
||||
storage::PropertyValue(),
|
||||
storage::PropertyValue(storage::TemporalData(storage::TemporalType::Date, 23))};
|
||||
ASSERT_EQ(vec_v[0].type(), storage::PropertyValue::Type::String);
|
||||
ASSERT_EQ(vec_v[1].type(), storage::PropertyValue::Type::Int);
|
||||
ASSERT_EQ(vec_v[2].type(), storage::PropertyValue::Type::Double);
|
||||
ASSERT_EQ(vec_v[3].type(), storage::PropertyValue::Type::Bool);
|
||||
ASSERT_EQ(vec_v[4].type(), storage::PropertyValue::Type::Null);
|
||||
ASSERT_EQ(vec_v[5].type(), storage::PropertyValue::Type::TemporalData);
|
||||
|
||||
std::map<std::string, storage::PropertyValue> map_v{{"hello", storage::PropertyValue("world")},
|
||||
{"number", storage::PropertyValue(5)},
|
||||
{"real", storage::PropertyValue(1.123423)},
|
||||
{"truth", storage::PropertyValue(true)},
|
||||
{"nothing", storage::PropertyValue()}};
|
||||
std::map<std::string, storage::PropertyValue> map_v{
|
||||
{"hello", storage::PropertyValue("world")},
|
||||
{"number", storage::PropertyValue(5)},
|
||||
{"real", storage::PropertyValue(1.123423)},
|
||||
{"truth", storage::PropertyValue(true)},
|
||||
{"nothing", storage::PropertyValue()},
|
||||
{"date", storage::PropertyValue(storage::TemporalData(storage::TemporalType::Date, 23))}};
|
||||
ASSERT_EQ(map_v["hello"].type(), storage::PropertyValue::Type::String);
|
||||
ASSERT_EQ(map_v["number"].type(), storage::PropertyValue::Type::Int);
|
||||
ASSERT_EQ(map_v["real"].type(), storage::PropertyValue::Type::Double);
|
||||
ASSERT_EQ(map_v["truth"].type(), storage::PropertyValue::Type::Bool);
|
||||
ASSERT_EQ(map_v["nothing"].type(), storage::PropertyValue::Type::Null);
|
||||
ASSERT_EQ(map_v["date"].type(), storage::PropertyValue::Type::TemporalData);
|
||||
|
||||
storage::PropertyValue original(
|
||||
std::vector<storage::PropertyValue>{storage::PropertyValue(vec_v), storage::PropertyValue(map_v)});
|
||||
|
@ -4,6 +4,8 @@
|
||||
#include <limits>
|
||||
|
||||
#include "storage/v2/durability/serialization.hpp"
|
||||
#include "storage/v2/property_value.hpp"
|
||||
#include "storage/v2/temporal.hpp"
|
||||
|
||||
static const std::string kTestMagic{"MGtest"};
|
||||
static const uint64_t kTestVersion{1};
|
||||
@ -118,7 +120,8 @@ GENERATE_READ_TEST(PropertyValue, storage::PropertyValue, storage::PropertyValue
|
||||
storage::PropertyValue(std::vector<storage::PropertyValue>{storage::PropertyValue("nandare"),
|
||||
storage::PropertyValue(123L)}),
|
||||
storage::PropertyValue(std::map<std::string, storage::PropertyValue>{
|
||||
{"nandare", storage::PropertyValue(123)}}));
|
||||
{"nandare", storage::PropertyValue(123)}}),
|
||||
storage::PropertyValue(storage::TemporalData(storage::TemporalType::Date, 23)));
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
|
||||
#define GENERATE_SKIP_TEST(name, type, ...) \
|
||||
@ -162,7 +165,8 @@ GENERATE_SKIP_TEST(PropertyValue, storage::PropertyValue, storage::PropertyValue
|
||||
storage::PropertyValue(std::vector<storage::PropertyValue>{storage::PropertyValue("nandare"),
|
||||
storage::PropertyValue(123L)}),
|
||||
storage::PropertyValue(std::map<std::string, storage::PropertyValue>{
|
||||
{"nandare", storage::PropertyValue(123)}}));
|
||||
{"nandare", storage::PropertyValue(123)}}),
|
||||
storage::PropertyValue(storage::TemporalData(storage::TemporalType::Date, 23)));
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
|
||||
#define GENERATE_PARTIAL_READ_TEST(name, value) \
|
||||
@ -225,8 +229,9 @@ GENERATE_PARTIAL_READ_TEST(PropertyValue,
|
||||
storage::PropertyValue(std::vector<storage::PropertyValue>{
|
||||
storage::PropertyValue(), storage::PropertyValue(true), storage::PropertyValue(123L),
|
||||
storage::PropertyValue(123.5), storage::PropertyValue("nandare"),
|
||||
storage::PropertyValue{std::map<std::string, storage::PropertyValue>{
|
||||
{"haihai", storage::PropertyValue()}}}}));
|
||||
storage::PropertyValue{
|
||||
std::map<std::string, storage::PropertyValue>{{"haihai", storage::PropertyValue()}}},
|
||||
storage::PropertyValue(storage::TemporalData(storage::TemporalType::Date, 23))}));
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
|
||||
#define GENERATE_PARTIAL_SKIP_TEST(name, value) \
|
||||
@ -275,8 +280,9 @@ GENERATE_PARTIAL_SKIP_TEST(PropertyValue,
|
||||
storage::PropertyValue(std::vector<storage::PropertyValue>{
|
||||
storage::PropertyValue(), storage::PropertyValue(true), storage::PropertyValue(123L),
|
||||
storage::PropertyValue(123.5), storage::PropertyValue("nandare"),
|
||||
storage::PropertyValue{std::map<std::string, storage::PropertyValue>{
|
||||
{"haihai", storage::PropertyValue()}}}}));
|
||||
storage::PropertyValue{
|
||||
std::map<std::string, storage::PropertyValue>{{"haihai", storage::PropertyValue()}}},
|
||||
storage::PropertyValue(storage::TemporalData(storage::TemporalType::Date, 23))}));
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST_F(DecoderEncoderTest, PropertyValueInvalidMarker) {
|
||||
@ -299,6 +305,7 @@ TEST_F(DecoderEncoderTest, PropertyValueInvalidMarker) {
|
||||
case storage::durability::Marker::TYPE_STRING:
|
||||
case storage::durability::Marker::TYPE_LIST:
|
||||
case storage::durability::Marker::TYPE_MAP:
|
||||
case storage::durability::Marker::TYPE_TEMPORAL_DATA:
|
||||
case storage::durability::Marker::TYPE_PROPERTY_VALUE:
|
||||
valid_marker = true;
|
||||
break;
|
||||
|
@ -1,7 +1,9 @@
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "storage/v2/property_value.hpp"
|
||||
#include "storage/v2/storage.hpp"
|
||||
#include "storage/v2/temporal.hpp"
|
||||
|
||||
// NOLINTNEXTLINE(google-build-using-namespace)
|
||||
using namespace storage;
|
||||
@ -614,6 +616,9 @@ TEST_F(IndexTest, LabelPropertyIndexCountEstimate) {
|
||||
TEST_F(IndexTest, LabelPropertyIndexMixedIteration) {
|
||||
storage.CreateIndex(label1, prop_val);
|
||||
|
||||
const std::array temporals{TemporalData{TemporalType::Date, 23}, TemporalData{TemporalType::Date, 28},
|
||||
TemporalData{TemporalType::LocalDateTime, 20}};
|
||||
|
||||
std::vector<PropertyValue> values = {
|
||||
PropertyValue(false),
|
||||
PropertyValue(true),
|
||||
@ -638,6 +643,9 @@ TEST_F(IndexTest, LabelPropertyIndexMixedIteration) {
|
||||
PropertyValue(std::map<std::string, PropertyValue>()),
|
||||
PropertyValue(std::map<std::string, PropertyValue>{{"id", PropertyValue(5)}}),
|
||||
PropertyValue(std::map<std::string, PropertyValue>{{"id", PropertyValue(10)}}),
|
||||
PropertyValue(temporals[0]),
|
||||
PropertyValue(temporals[1]),
|
||||
PropertyValue(temporals[2]),
|
||||
};
|
||||
|
||||
// Create vertices, each with one of the values above.
|
||||
@ -735,6 +743,17 @@ TEST_F(IndexTest, LabelPropertyIndexMixedIteration) {
|
||||
{PropertyValue(std::map<std::string, PropertyValue>{{"id", PropertyValue(5)}}),
|
||||
PropertyValue(std::map<std::string, PropertyValue>{{"id", PropertyValue(10)}})});
|
||||
|
||||
verify(utils::MakeBoundExclusive(PropertyValue(temporals[0])),
|
||||
utils::MakeBoundInclusive(PropertyValue(TemporalData{TemporalType::Date, 200})),
|
||||
// LocalDateTime has a "higher" type number so it is not part of the range
|
||||
{PropertyValue(temporals[1])});
|
||||
verify(utils::MakeBoundExclusive(PropertyValue(temporals[0])), utils::MakeBoundInclusive(PropertyValue(temporals[2])),
|
||||
{PropertyValue(temporals[1]), PropertyValue(temporals[2])});
|
||||
verify(utils::MakeBoundInclusive(PropertyValue(temporals[0])), utils::MakeBoundExclusive(PropertyValue(temporals[2])),
|
||||
{PropertyValue(temporals[0]), PropertyValue(temporals[1])});
|
||||
verify(utils::MakeBoundInclusive(PropertyValue(temporals[0])), utils::MakeBoundInclusive(PropertyValue(temporals[2])),
|
||||
{PropertyValue(temporals[0]), PropertyValue(temporals[1]), PropertyValue(temporals[2])});
|
||||
|
||||
// Range iteration with one unspecified bound should only yield items that
|
||||
// have the same type as the specified bound.
|
||||
verify(utils::MakeBoundInclusive(PropertyValue(false)), std::nullopt, {PropertyValue(false), PropertyValue(true)});
|
||||
@ -760,6 +779,10 @@ TEST_F(IndexTest, LabelPropertyIndexMixedIteration) {
|
||||
utils::MakeBoundExclusive(PropertyValue(std::map<std::string, PropertyValue>{{"id", PropertyValue(7.5)}})),
|
||||
{PropertyValue(std::map<std::string, PropertyValue>()),
|
||||
PropertyValue(std::map<std::string, PropertyValue>{{"id", PropertyValue(5)}})});
|
||||
verify(utils::MakeBoundInclusive(PropertyValue(TemporalData(TemporalType::Date, 10))), std::nullopt,
|
||||
{PropertyValue(temporals[0]), PropertyValue(temporals[1]), PropertyValue(temporals[2])});
|
||||
verify(std::nullopt, utils::MakeBoundExclusive(PropertyValue(TemporalData(TemporalType::Duration, 0))),
|
||||
{PropertyValue(temporals[0]), PropertyValue(temporals[1]), PropertyValue(temporals[2])});
|
||||
|
||||
// Range iteration with two specified bounds that don't have the same type
|
||||
// should yield no items.
|
||||
|
@ -4,6 +4,8 @@
|
||||
#include <limits>
|
||||
|
||||
#include "storage/v2/property_store.hpp"
|
||||
#include "storage/v2/property_value.hpp"
|
||||
#include "storage/v2/temporal.hpp"
|
||||
|
||||
using testing::UnorderedElementsAre;
|
||||
|
||||
@ -37,6 +39,7 @@ const storage::PropertyValue kSampleValues[] = {
|
||||
std::map<std::string, storage::PropertyValue>{{"test", storage::PropertyValue(33)},
|
||||
{"map", storage::PropertyValue(std::string("sample"))},
|
||||
{"item", storage::PropertyValue(-33.33)}}),
|
||||
storage::PropertyValue(storage::TemporalData(storage::TemporalType::Date, 23)),
|
||||
};
|
||||
|
||||
void TestIsPropertyEqual(const storage::PropertyStore &store, storage::PropertyId property,
|
||||
@ -71,12 +74,22 @@ TEST(PropertyStore, Simple) {
|
||||
TEST(PropertyStore, SimpleLarge) {
|
||||
storage::PropertyStore props;
|
||||
auto prop = storage::PropertyId::FromInt(42);
|
||||
auto value = storage::PropertyValue(std::string(10000, 'a'));
|
||||
ASSERT_TRUE(props.SetProperty(prop, value));
|
||||
ASSERT_EQ(props.GetProperty(prop), value);
|
||||
ASSERT_TRUE(props.HasProperty(prop));
|
||||
TestIsPropertyEqual(props, prop, value);
|
||||
ASSERT_THAT(props.Properties(), UnorderedElementsAre(std::pair(prop, value)));
|
||||
{
|
||||
auto value = storage::PropertyValue(std::string(10000, 'a'));
|
||||
ASSERT_TRUE(props.SetProperty(prop, value));
|
||||
ASSERT_EQ(props.GetProperty(prop), value);
|
||||
ASSERT_TRUE(props.HasProperty(prop));
|
||||
TestIsPropertyEqual(props, prop, value);
|
||||
ASSERT_THAT(props.Properties(), UnorderedElementsAre(std::pair(prop, value)));
|
||||
}
|
||||
{
|
||||
auto value = storage::PropertyValue(storage::TemporalData(storage::TemporalType::Date, 23));
|
||||
ASSERT_FALSE(props.SetProperty(prop, value));
|
||||
ASSERT_EQ(props.GetProperty(prop), value);
|
||||
ASSERT_TRUE(props.HasProperty(prop));
|
||||
TestIsPropertyEqual(props, prop, value);
|
||||
ASSERT_THAT(props.Properties(), UnorderedElementsAre(std::pair(prop, value)));
|
||||
}
|
||||
|
||||
ASSERT_FALSE(props.SetProperty(prop, storage::PropertyValue()));
|
||||
ASSERT_TRUE(props.GetProperty(prop).IsNull());
|
||||
@ -227,9 +240,11 @@ TEST(PropertyStore, EmptySet) {
|
||||
std::vector<storage::PropertyValue> vec{storage::PropertyValue(true), storage::PropertyValue(123),
|
||||
storage::PropertyValue()};
|
||||
std::map<std::string, storage::PropertyValue> map{{"nandare", storage::PropertyValue(false)}};
|
||||
std::vector<storage::PropertyValue> data{storage::PropertyValue(true), storage::PropertyValue(123),
|
||||
storage::PropertyValue(123.5), storage::PropertyValue("nandare"),
|
||||
storage::PropertyValue(vec), storage::PropertyValue(map)};
|
||||
const storage::TemporalData temporal{storage::TemporalType::LocalDateTime, 23};
|
||||
std::vector<storage::PropertyValue> data{storage::PropertyValue(true), storage::PropertyValue(123),
|
||||
storage::PropertyValue(123.5), storage::PropertyValue("nandare"),
|
||||
storage::PropertyValue(vec), storage::PropertyValue(map),
|
||||
storage::PropertyValue(temporal)};
|
||||
|
||||
auto prop = storage::PropertyId::FromInt(42);
|
||||
for (const auto &value : data) {
|
||||
@ -262,13 +277,15 @@ TEST(PropertyStore, FullSet) {
|
||||
std::vector<storage::PropertyValue> vec{storage::PropertyValue(true), storage::PropertyValue(123),
|
||||
storage::PropertyValue()};
|
||||
std::map<std::string, storage::PropertyValue> map{{"nandare", storage::PropertyValue(false)}};
|
||||
const storage::TemporalData temporal{storage::TemporalType::LocalDateTime, 23};
|
||||
std::map<storage::PropertyId, storage::PropertyValue> data{
|
||||
{storage::PropertyId::FromInt(1), storage::PropertyValue(true)},
|
||||
{storage::PropertyId::FromInt(2), storage::PropertyValue(123)},
|
||||
{storage::PropertyId::FromInt(3), storage::PropertyValue(123.5)},
|
||||
{storage::PropertyId::FromInt(4), storage::PropertyValue("nandare")},
|
||||
{storage::PropertyId::FromInt(5), storage::PropertyValue(vec)},
|
||||
{storage::PropertyId::FromInt(6), storage::PropertyValue(map)}};
|
||||
{storage::PropertyId::FromInt(6), storage::PropertyValue(map)},
|
||||
{storage::PropertyId::FromInt(7), storage::PropertyValue(temporal)}};
|
||||
|
||||
std::vector<storage::PropertyValue> alt{storage::PropertyValue(),
|
||||
storage::PropertyValue(std::string()),
|
||||
@ -585,3 +602,19 @@ TEST(PropertyStore, IsPropertyEqualMap) {
|
||||
{"sdf", storage::PropertyValue(true)},
|
||||
{"zyx", storage::PropertyValue("test")}})));
|
||||
}
|
||||
|
||||
TEST(PropertyStore, IsPropertyEqualTemporalData) {
|
||||
storage::PropertyStore props;
|
||||
auto prop = storage::PropertyId::FromInt(42);
|
||||
const storage::TemporalData temporal{storage::TemporalType::Date, 23};
|
||||
ASSERT_TRUE(props.SetProperty(prop, storage::PropertyValue(temporal)));
|
||||
ASSERT_TRUE(props.IsPropertyEqual(prop, storage::PropertyValue(temporal)));
|
||||
|
||||
// Different type.
|
||||
ASSERT_FALSE(
|
||||
props.IsPropertyEqual(prop, storage::PropertyValue(storage::TemporalData{storage::TemporalType::Duration, 23})));
|
||||
|
||||
// Same type, different value.
|
||||
ASSERT_FALSE(
|
||||
props.IsPropertyEqual(prop, storage::PropertyValue(storage::TemporalData{storage::TemporalType::Date, 30})));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user