Add TemporalData to PropertyValue (#174)

This commit is contained in:
antonio2368 2021-06-21 16:42:58 +02:00 committed by Antonio Andelic
parent ccca46370d
commit 5da32c1bff
27 changed files with 482 additions and 95 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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(&microseconds, 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

View File

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

View 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

View 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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