Implement properties for edges in storage v2
Reviewers: mtomic, teon.banek Reviewed By: mtomic Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D2187
This commit is contained in:
parent
5b8e7ff432
commit
b1f7bbf051
@ -20,8 +20,7 @@ struct Edge {
|
||||
|
||||
Gid gid;
|
||||
|
||||
// TODO: add
|
||||
// std::unordered_map<uint64_t, storage::PropertyValue> properties;
|
||||
std::unordered_map<uint64_t, storage::PropertyValue> properties;
|
||||
|
||||
utils::SpinLock lock;
|
||||
bool deleted;
|
||||
|
@ -15,4 +15,133 @@ VertexAccessor EdgeAccessor::ToVertex() {
|
||||
return VertexAccessor{to_vertex_, transaction_};
|
||||
}
|
||||
|
||||
Result<bool> EdgeAccessor::SetProperty(uint64_t property,
|
||||
const PropertyValue &value) {
|
||||
std::lock_guard<utils::SpinLock> guard(edge_->lock);
|
||||
|
||||
if (!PrepareForWrite(transaction_, edge_))
|
||||
return Result<bool>{Error::SERIALIZATION_ERROR};
|
||||
|
||||
if (edge_->deleted) return Result<bool>{Error::DELETED_OBJECT};
|
||||
|
||||
auto it = edge_->properties.find(property);
|
||||
bool existed = it != edge_->properties.end();
|
||||
if (it != edge_->properties.end()) {
|
||||
CreateAndLinkDelta(transaction_, edge_, Delta::SetPropertyTag(), property,
|
||||
it->second);
|
||||
if (value.IsNull()) {
|
||||
// remove the property
|
||||
edge_->properties.erase(it);
|
||||
} else {
|
||||
// set the value
|
||||
it->second = value;
|
||||
}
|
||||
} else {
|
||||
CreateAndLinkDelta(transaction_, edge_, Delta::SetPropertyTag(), property,
|
||||
PropertyValue());
|
||||
if (!value.IsNull()) {
|
||||
edge_->properties.emplace(property, value);
|
||||
}
|
||||
}
|
||||
|
||||
return Result<bool>{existed};
|
||||
}
|
||||
|
||||
Result<PropertyValue> EdgeAccessor::GetProperty(uint64_t property, View view) {
|
||||
bool deleted = false;
|
||||
PropertyValue value;
|
||||
Delta *delta = nullptr;
|
||||
{
|
||||
std::lock_guard<utils::SpinLock> guard(edge_->lock);
|
||||
deleted = edge_->deleted;
|
||||
auto it = edge_->properties.find(property);
|
||||
if (it != edge_->properties.end()) {
|
||||
value = it->second;
|
||||
}
|
||||
delta = edge_->delta;
|
||||
}
|
||||
ApplyDeltasForRead(transaction_, delta, view,
|
||||
[&deleted, &value, property](const Delta &delta) {
|
||||
switch (delta.action) {
|
||||
case Delta::Action::SET_PROPERTY: {
|
||||
if (delta.property.key == property) {
|
||||
value = delta.property.value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Delta::Action::DELETE_OBJECT: {
|
||||
LOG(FATAL) << "Invalid accessor!";
|
||||
break;
|
||||
}
|
||||
case Delta::Action::RECREATE_OBJECT: {
|
||||
deleted = false;
|
||||
break;
|
||||
}
|
||||
case Delta::Action::ADD_LABEL:
|
||||
case Delta::Action::REMOVE_LABEL:
|
||||
case Delta::Action::ADD_IN_EDGE:
|
||||
case Delta::Action::ADD_OUT_EDGE:
|
||||
case Delta::Action::REMOVE_IN_EDGE:
|
||||
case Delta::Action::REMOVE_OUT_EDGE:
|
||||
break;
|
||||
}
|
||||
});
|
||||
if (deleted) return Result<PropertyValue>{Error::DELETED_OBJECT};
|
||||
return Result<PropertyValue>{std::move(value)};
|
||||
}
|
||||
|
||||
Result<std::unordered_map<uint64_t, PropertyValue>> EdgeAccessor::Properties(
|
||||
View view) {
|
||||
std::unordered_map<uint64_t, PropertyValue> properties;
|
||||
bool deleted = false;
|
||||
Delta *delta = nullptr;
|
||||
{
|
||||
std::lock_guard<utils::SpinLock> guard(edge_->lock);
|
||||
deleted = edge_->deleted;
|
||||
properties = edge_->properties;
|
||||
delta = edge_->delta;
|
||||
}
|
||||
ApplyDeltasForRead(
|
||||
transaction_, delta, view, [&deleted, &properties](const Delta &delta) {
|
||||
switch (delta.action) {
|
||||
case Delta::Action::SET_PROPERTY: {
|
||||
auto it = properties.find(delta.property.key);
|
||||
if (it != properties.end()) {
|
||||
if (delta.property.value.IsNull()) {
|
||||
// remove the property
|
||||
properties.erase(it);
|
||||
} else {
|
||||
// set the value
|
||||
it->second = delta.property.value;
|
||||
}
|
||||
} else if (!delta.property.value.IsNull()) {
|
||||
properties.emplace(delta.property.key, delta.property.value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Delta::Action::DELETE_OBJECT: {
|
||||
LOG(FATAL) << "Invalid accessor!";
|
||||
break;
|
||||
}
|
||||
case Delta::Action::RECREATE_OBJECT: {
|
||||
deleted = false;
|
||||
break;
|
||||
}
|
||||
case Delta::Action::ADD_LABEL:
|
||||
case Delta::Action::REMOVE_LABEL:
|
||||
case Delta::Action::ADD_IN_EDGE:
|
||||
case Delta::Action::ADD_OUT_EDGE:
|
||||
case Delta::Action::REMOVE_IN_EDGE:
|
||||
case Delta::Action::REMOVE_OUT_EDGE:
|
||||
break;
|
||||
}
|
||||
});
|
||||
if (deleted) {
|
||||
return Result<std::unordered_map<uint64_t, PropertyValue>>{
|
||||
Error::DELETED_OBJECT};
|
||||
}
|
||||
return Result<std::unordered_map<uint64_t, PropertyValue>>{
|
||||
std::move(properties)};
|
||||
}
|
||||
|
||||
} // namespace storage
|
||||
|
@ -33,6 +33,12 @@ class EdgeAccessor final {
|
||||
|
||||
uint64_t EdgeType() const { return edge_type_; }
|
||||
|
||||
Result<bool> SetProperty(uint64_t property, const PropertyValue &value);
|
||||
|
||||
Result<PropertyValue> GetProperty(uint64_t property, View view);
|
||||
|
||||
Result<std::unordered_map<uint64_t, PropertyValue>> Properties(View view);
|
||||
|
||||
Gid Gid() const { return edge_->gid; }
|
||||
|
||||
bool operator==(const EdgeAccessor &other) const {
|
||||
|
@ -407,6 +407,22 @@ void Storage::Accessor::Abort() {
|
||||
current->timestamp->load(std::memory_order_acquire) ==
|
||||
transaction_->transaction_id) {
|
||||
switch (current->action) {
|
||||
case Delta::Action::SET_PROPERTY: {
|
||||
auto it = edge->properties.find(current->property.key);
|
||||
if (it != edge->properties.end()) {
|
||||
if (current->property.value.IsNull()) {
|
||||
// remove the property
|
||||
edge->properties.erase(it);
|
||||
} else {
|
||||
// set the value
|
||||
it->second = current->property.value;
|
||||
}
|
||||
} else if (!current->property.value.IsNull()) {
|
||||
edge->properties.emplace(current->property.key,
|
||||
current->property.value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Delta::Action::DELETE_OBJECT: {
|
||||
auto acc = storage_->edges_.access();
|
||||
CHECK(acc.remove(edge->gid)) << "Invalid database state!";
|
||||
@ -418,7 +434,6 @@ void Storage::Accessor::Abort() {
|
||||
}
|
||||
case Delta::Action::REMOVE_LABEL:
|
||||
case Delta::Action::ADD_LABEL:
|
||||
case Delta::Action::SET_PROPERTY:
|
||||
case Delta::Action::ADD_IN_EDGE:
|
||||
case Delta::Action::ADD_OUT_EDGE:
|
||||
case Delta::Action::REMOVE_IN_EDGE:
|
||||
|
@ -4622,3 +4622,525 @@ TEST(StorageV2, VertexDetachDeleteMultipleAbort) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST(StorageV2, EdgePropertyCommit) {
|
||||
storage::Storage store;
|
||||
storage::Gid gid =
|
||||
storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
|
||||
{
|
||||
auto acc = store.Access();
|
||||
auto vertex = acc.CreateVertex();
|
||||
gid = vertex.Gid();
|
||||
auto edge = acc.CreateEdge(&vertex, &vertex, 5).GetReturn();
|
||||
ASSERT_EQ(edge.EdgeType(), 5);
|
||||
ASSERT_EQ(edge.FromVertex(), vertex);
|
||||
ASSERT_EQ(edge.ToVertex(), vertex);
|
||||
|
||||
ASSERT_TRUE(edge.GetProperty(5, storage::View::NEW).GetReturn().IsNull());
|
||||
ASSERT_EQ(edge.Properties(storage::View::NEW).GetReturn().size(), 0);
|
||||
|
||||
{
|
||||
auto res = edge.SetProperty(5, storage::PropertyValue("temporary"));
|
||||
ASSERT_TRUE(res.IsReturn());
|
||||
ASSERT_FALSE(res.GetReturn());
|
||||
}
|
||||
|
||||
ASSERT_EQ(edge.GetProperty(5, storage::View::NEW).GetReturn().ValueString(),
|
||||
"temporary");
|
||||
{
|
||||
auto properties = edge.Properties(storage::View::NEW).GetReturn();
|
||||
ASSERT_EQ(properties.size(), 1);
|
||||
ASSERT_EQ(properties[5].ValueString(), "temporary");
|
||||
}
|
||||
|
||||
{
|
||||
auto res = edge.SetProperty(5, storage::PropertyValue("nandare"));
|
||||
ASSERT_TRUE(res.IsReturn());
|
||||
ASSERT_TRUE(res.GetReturn());
|
||||
}
|
||||
|
||||
ASSERT_EQ(edge.GetProperty(5, storage::View::NEW).GetReturn().ValueString(),
|
||||
"nandare");
|
||||
{
|
||||
auto properties = edge.Properties(storage::View::NEW).GetReturn();
|
||||
ASSERT_EQ(properties.size(), 1);
|
||||
ASSERT_EQ(properties[5].ValueString(), "nandare");
|
||||
}
|
||||
|
||||
acc.Commit();
|
||||
}
|
||||
{
|
||||
auto acc = store.Access();
|
||||
auto vertex = acc.FindVertex(gid, storage::View::OLD);
|
||||
ASSERT_TRUE(vertex);
|
||||
auto [edge_type, other_vertex, edge] =
|
||||
vertex->OutEdges({}, storage::View::NEW).GetReturn()[0];
|
||||
|
||||
ASSERT_EQ(edge.GetProperty(5, storage::View::OLD).GetReturn().ValueString(),
|
||||
"nandare");
|
||||
{
|
||||
auto properties = edge.Properties(storage::View::OLD).GetReturn();
|
||||
ASSERT_EQ(properties.size(), 1);
|
||||
ASSERT_EQ(properties[5].ValueString(), "nandare");
|
||||
}
|
||||
|
||||
ASSERT_EQ(edge.GetProperty(5, storage::View::NEW).GetReturn().ValueString(),
|
||||
"nandare");
|
||||
{
|
||||
auto properties = edge.Properties(storage::View::NEW).GetReturn();
|
||||
ASSERT_EQ(properties.size(), 1);
|
||||
ASSERT_EQ(properties[5].ValueString(), "nandare");
|
||||
}
|
||||
|
||||
ASSERT_TRUE(edge.GetProperty(10, storage::View::OLD).GetReturn().IsNull());
|
||||
ASSERT_TRUE(edge.GetProperty(10, storage::View::NEW).GetReturn().IsNull());
|
||||
|
||||
acc.Abort();
|
||||
}
|
||||
{
|
||||
auto acc = store.Access();
|
||||
auto vertex = acc.FindVertex(gid, storage::View::OLD);
|
||||
ASSERT_TRUE(vertex);
|
||||
auto [edge_type, other_vertex, edge] =
|
||||
vertex->OutEdges({}, storage::View::NEW).GetReturn()[0];
|
||||
|
||||
{
|
||||
auto res = edge.SetProperty(5, storage::PropertyValue());
|
||||
ASSERT_TRUE(res.IsReturn());
|
||||
ASSERT_TRUE(res.GetReturn());
|
||||
}
|
||||
|
||||
ASSERT_EQ(edge.GetProperty(5, storage::View::OLD).GetReturn().ValueString(),
|
||||
"nandare");
|
||||
{
|
||||
auto properties = edge.Properties(storage::View::OLD).GetReturn();
|
||||
ASSERT_EQ(properties.size(), 1);
|
||||
ASSERT_EQ(properties[5].ValueString(), "nandare");
|
||||
}
|
||||
|
||||
ASSERT_TRUE(edge.GetProperty(5, storage::View::NEW).GetReturn().IsNull());
|
||||
ASSERT_EQ(edge.Properties(storage::View::NEW).GetReturn().size(), 0);
|
||||
|
||||
{
|
||||
auto res = edge.SetProperty(5, storage::PropertyValue());
|
||||
ASSERT_TRUE(res.IsReturn());
|
||||
ASSERT_FALSE(res.GetReturn());
|
||||
}
|
||||
|
||||
acc.Commit();
|
||||
}
|
||||
{
|
||||
auto acc = store.Access();
|
||||
auto vertex = acc.FindVertex(gid, storage::View::OLD);
|
||||
ASSERT_TRUE(vertex);
|
||||
auto [edge_type, other_vertex, edge] =
|
||||
vertex->OutEdges({}, storage::View::NEW).GetReturn()[0];
|
||||
|
||||
ASSERT_TRUE(edge.GetProperty(5, storage::View::OLD).GetReturn().IsNull());
|
||||
ASSERT_TRUE(edge.GetProperty(5, storage::View::NEW).GetReturn().IsNull());
|
||||
ASSERT_EQ(edge.Properties(storage::View::OLD).GetReturn().size(), 0);
|
||||
ASSERT_EQ(edge.Properties(storage::View::NEW).GetReturn().size(), 0);
|
||||
|
||||
ASSERT_TRUE(edge.GetProperty(10, storage::View::OLD).GetReturn().IsNull());
|
||||
ASSERT_TRUE(edge.GetProperty(10, storage::View::NEW).GetReturn().IsNull());
|
||||
|
||||
acc.Abort();
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST(StorageV2, EdgePropertyAbort) {
|
||||
storage::Storage store;
|
||||
storage::Gid gid =
|
||||
storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
|
||||
|
||||
// Create the vertex.
|
||||
{
|
||||
auto acc = store.Access();
|
||||
auto vertex = acc.CreateVertex();
|
||||
gid = vertex.Gid();
|
||||
auto edge = acc.CreateEdge(&vertex, &vertex, 5).GetReturn();
|
||||
ASSERT_EQ(edge.EdgeType(), 5);
|
||||
ASSERT_EQ(edge.FromVertex(), vertex);
|
||||
ASSERT_EQ(edge.ToVertex(), vertex);
|
||||
acc.Commit();
|
||||
}
|
||||
|
||||
// Set property 5 to "nandare", but abort the transaction.
|
||||
{
|
||||
auto acc = store.Access();
|
||||
auto vertex = acc.FindVertex(gid, storage::View::OLD);
|
||||
ASSERT_TRUE(vertex);
|
||||
auto [edge_type, other_vertex, edge] =
|
||||
vertex->OutEdges({}, storage::View::NEW).GetReturn()[0];
|
||||
|
||||
ASSERT_TRUE(edge.GetProperty(5, storage::View::NEW).GetReturn().IsNull());
|
||||
ASSERT_EQ(edge.Properties(storage::View::NEW).GetReturn().size(), 0);
|
||||
|
||||
{
|
||||
auto res = edge.SetProperty(5, storage::PropertyValue("temporary"));
|
||||
ASSERT_TRUE(res.IsReturn());
|
||||
ASSERT_FALSE(res.GetReturn());
|
||||
}
|
||||
|
||||
ASSERT_EQ(edge.GetProperty(5, storage::View::NEW).GetReturn().ValueString(),
|
||||
"temporary");
|
||||
{
|
||||
auto properties = edge.Properties(storage::View::NEW).GetReturn();
|
||||
ASSERT_EQ(properties.size(), 1);
|
||||
ASSERT_EQ(properties[5].ValueString(), "temporary");
|
||||
}
|
||||
|
||||
{
|
||||
auto res = edge.SetProperty(5, storage::PropertyValue("nandare"));
|
||||
ASSERT_TRUE(res.IsReturn());
|
||||
ASSERT_TRUE(res.GetReturn());
|
||||
}
|
||||
|
||||
ASSERT_EQ(edge.GetProperty(5, storage::View::NEW).GetReturn().ValueString(),
|
||||
"nandare");
|
||||
{
|
||||
auto properties = edge.Properties(storage::View::NEW).GetReturn();
|
||||
ASSERT_EQ(properties.size(), 1);
|
||||
ASSERT_EQ(properties[5].ValueString(), "nandare");
|
||||
}
|
||||
|
||||
acc.Abort();
|
||||
}
|
||||
|
||||
// Check that property 5 is null.
|
||||
{
|
||||
auto acc = store.Access();
|
||||
auto vertex = acc.FindVertex(gid, storage::View::OLD);
|
||||
ASSERT_TRUE(vertex);
|
||||
auto [edge_type, other_vertex, edge] =
|
||||
vertex->OutEdges({}, storage::View::NEW).GetReturn()[0];
|
||||
|
||||
ASSERT_TRUE(edge.GetProperty(5, storage::View::OLD).GetReturn().IsNull());
|
||||
ASSERT_TRUE(edge.GetProperty(5, storage::View::NEW).GetReturn().IsNull());
|
||||
ASSERT_EQ(edge.Properties(storage::View::OLD).GetReturn().size(), 0);
|
||||
ASSERT_EQ(edge.Properties(storage::View::NEW).GetReturn().size(), 0);
|
||||
|
||||
ASSERT_TRUE(edge.GetProperty(10, storage::View::OLD).GetReturn().IsNull());
|
||||
ASSERT_TRUE(edge.GetProperty(10, storage::View::NEW).GetReturn().IsNull());
|
||||
|
||||
acc.Abort();
|
||||
}
|
||||
|
||||
// Set property 5 to "nandare".
|
||||
{
|
||||
auto acc = store.Access();
|
||||
auto vertex = acc.FindVertex(gid, storage::View::OLD);
|
||||
ASSERT_TRUE(vertex);
|
||||
auto [edge_type, other_vertex, edge] =
|
||||
vertex->OutEdges({}, storage::View::NEW).GetReturn()[0];
|
||||
|
||||
ASSERT_TRUE(edge.GetProperty(5, storage::View::NEW).GetReturn().IsNull());
|
||||
ASSERT_EQ(edge.Properties(storage::View::NEW).GetReturn().size(), 0);
|
||||
|
||||
{
|
||||
auto res = edge.SetProperty(5, storage::PropertyValue("temporary"));
|
||||
ASSERT_TRUE(res.IsReturn());
|
||||
ASSERT_FALSE(res.GetReturn());
|
||||
}
|
||||
|
||||
ASSERT_EQ(edge.GetProperty(5, storage::View::NEW).GetReturn().ValueString(),
|
||||
"temporary");
|
||||
{
|
||||
auto properties = edge.Properties(storage::View::NEW).GetReturn();
|
||||
ASSERT_EQ(properties.size(), 1);
|
||||
ASSERT_EQ(properties[5].ValueString(), "temporary");
|
||||
}
|
||||
|
||||
{
|
||||
auto res = edge.SetProperty(5, storage::PropertyValue("nandare"));
|
||||
ASSERT_TRUE(res.IsReturn());
|
||||
ASSERT_TRUE(res.GetReturn());
|
||||
}
|
||||
|
||||
ASSERT_EQ(edge.GetProperty(5, storage::View::NEW).GetReturn().ValueString(),
|
||||
"nandare");
|
||||
{
|
||||
auto properties = edge.Properties(storage::View::NEW).GetReturn();
|
||||
ASSERT_EQ(properties.size(), 1);
|
||||
ASSERT_EQ(properties[5].ValueString(), "nandare");
|
||||
}
|
||||
|
||||
acc.Commit();
|
||||
}
|
||||
|
||||
// Check that property 5 is "nandare".
|
||||
{
|
||||
auto acc = store.Access();
|
||||
auto vertex = acc.FindVertex(gid, storage::View::OLD);
|
||||
ASSERT_TRUE(vertex);
|
||||
auto [edge_type, other_vertex, edge] =
|
||||
vertex->OutEdges({}, storage::View::NEW).GetReturn()[0];
|
||||
|
||||
ASSERT_EQ(edge.GetProperty(5, storage::View::OLD).GetReturn().ValueString(),
|
||||
"nandare");
|
||||
{
|
||||
auto properties = edge.Properties(storage::View::OLD).GetReturn();
|
||||
ASSERT_EQ(properties.size(), 1);
|
||||
ASSERT_EQ(properties[5].ValueString(), "nandare");
|
||||
}
|
||||
|
||||
ASSERT_EQ(edge.GetProperty(5, storage::View::NEW).GetReturn().ValueString(),
|
||||
"nandare");
|
||||
{
|
||||
auto properties = edge.Properties(storage::View::NEW).GetReturn();
|
||||
ASSERT_EQ(properties.size(), 1);
|
||||
ASSERT_EQ(properties[5].ValueString(), "nandare");
|
||||
}
|
||||
|
||||
ASSERT_TRUE(edge.GetProperty(10, storage::View::OLD).GetReturn().IsNull());
|
||||
ASSERT_TRUE(edge.GetProperty(10, storage::View::NEW).GetReturn().IsNull());
|
||||
|
||||
acc.Abort();
|
||||
}
|
||||
|
||||
// Set property 5 to null, but abort the transaction.
|
||||
{
|
||||
auto acc = store.Access();
|
||||
auto vertex = acc.FindVertex(gid, storage::View::OLD);
|
||||
ASSERT_TRUE(vertex);
|
||||
auto [edge_type, other_vertex, edge] =
|
||||
vertex->OutEdges({}, storage::View::NEW).GetReturn()[0];
|
||||
|
||||
ASSERT_EQ(edge.GetProperty(5, storage::View::OLD).GetReturn().ValueString(),
|
||||
"nandare");
|
||||
{
|
||||
auto properties = edge.Properties(storage::View::OLD).GetReturn();
|
||||
ASSERT_EQ(properties.size(), 1);
|
||||
ASSERT_EQ(properties[5].ValueString(), "nandare");
|
||||
}
|
||||
|
||||
ASSERT_EQ(edge.GetProperty(5, storage::View::NEW).GetReturn().ValueString(),
|
||||
"nandare");
|
||||
{
|
||||
auto properties = edge.Properties(storage::View::NEW).GetReturn();
|
||||
ASSERT_EQ(properties.size(), 1);
|
||||
ASSERT_EQ(properties[5].ValueString(), "nandare");
|
||||
}
|
||||
|
||||
{
|
||||
auto res = edge.SetProperty(5, storage::PropertyValue());
|
||||
ASSERT_TRUE(res.IsReturn());
|
||||
ASSERT_TRUE(res.GetReturn());
|
||||
}
|
||||
|
||||
ASSERT_EQ(edge.GetProperty(5, storage::View::OLD).GetReturn().ValueString(),
|
||||
"nandare");
|
||||
{
|
||||
auto properties = edge.Properties(storage::View::OLD).GetReturn();
|
||||
ASSERT_EQ(properties.size(), 1);
|
||||
ASSERT_EQ(properties[5].ValueString(), "nandare");
|
||||
}
|
||||
|
||||
ASSERT_TRUE(edge.GetProperty(5, storage::View::NEW).GetReturn().IsNull());
|
||||
ASSERT_EQ(edge.Properties(storage::View::NEW).GetReturn().size(), 0);
|
||||
|
||||
acc.Abort();
|
||||
}
|
||||
|
||||
// Check that property 5 is "nandare".
|
||||
{
|
||||
auto acc = store.Access();
|
||||
auto vertex = acc.FindVertex(gid, storage::View::OLD);
|
||||
ASSERT_TRUE(vertex);
|
||||
auto [edge_type, other_vertex, edge] =
|
||||
vertex->OutEdges({}, storage::View::NEW).GetReturn()[0];
|
||||
|
||||
ASSERT_EQ(edge.GetProperty(5, storage::View::OLD).GetReturn().ValueString(),
|
||||
"nandare");
|
||||
{
|
||||
auto properties = edge.Properties(storage::View::OLD).GetReturn();
|
||||
ASSERT_EQ(properties.size(), 1);
|
||||
ASSERT_EQ(properties[5].ValueString(), "nandare");
|
||||
}
|
||||
|
||||
ASSERT_EQ(edge.GetProperty(5, storage::View::NEW).GetReturn().ValueString(),
|
||||
"nandare");
|
||||
{
|
||||
auto properties = edge.Properties(storage::View::NEW).GetReturn();
|
||||
ASSERT_EQ(properties.size(), 1);
|
||||
ASSERT_EQ(properties[5].ValueString(), "nandare");
|
||||
}
|
||||
|
||||
ASSERT_TRUE(edge.GetProperty(10, storage::View::OLD).GetReturn().IsNull());
|
||||
ASSERT_TRUE(edge.GetProperty(10, storage::View::NEW).GetReturn().IsNull());
|
||||
|
||||
acc.Abort();
|
||||
}
|
||||
|
||||
// Set property 5 to null.
|
||||
{
|
||||
auto acc = store.Access();
|
||||
auto vertex = acc.FindVertex(gid, storage::View::OLD);
|
||||
ASSERT_TRUE(vertex);
|
||||
auto [edge_type, other_vertex, edge] =
|
||||
vertex->OutEdges({}, storage::View::NEW).GetReturn()[0];
|
||||
|
||||
ASSERT_EQ(edge.GetProperty(5, storage::View::OLD).GetReturn().ValueString(),
|
||||
"nandare");
|
||||
{
|
||||
auto properties = edge.Properties(storage::View::OLD).GetReturn();
|
||||
ASSERT_EQ(properties.size(), 1);
|
||||
ASSERT_EQ(properties[5].ValueString(), "nandare");
|
||||
}
|
||||
|
||||
ASSERT_EQ(edge.GetProperty(5, storage::View::NEW).GetReturn().ValueString(),
|
||||
"nandare");
|
||||
{
|
||||
auto properties = edge.Properties(storage::View::NEW).GetReturn();
|
||||
ASSERT_EQ(properties.size(), 1);
|
||||
ASSERT_EQ(properties[5].ValueString(), "nandare");
|
||||
}
|
||||
|
||||
{
|
||||
auto res = edge.SetProperty(5, storage::PropertyValue());
|
||||
ASSERT_TRUE(res.IsReturn());
|
||||
ASSERT_TRUE(res.GetReturn());
|
||||
}
|
||||
|
||||
ASSERT_EQ(edge.GetProperty(5, storage::View::OLD).GetReturn().ValueString(),
|
||||
"nandare");
|
||||
{
|
||||
auto properties = edge.Properties(storage::View::OLD).GetReturn();
|
||||
ASSERT_EQ(properties.size(), 1);
|
||||
ASSERT_EQ(properties[5].ValueString(), "nandare");
|
||||
}
|
||||
|
||||
ASSERT_TRUE(edge.GetProperty(5, storage::View::NEW).GetReturn().IsNull());
|
||||
ASSERT_EQ(edge.Properties(storage::View::NEW).GetReturn().size(), 0);
|
||||
|
||||
acc.Commit();
|
||||
}
|
||||
|
||||
// Check that property 5 is null.
|
||||
{
|
||||
auto acc = store.Access();
|
||||
auto vertex = acc.FindVertex(gid, storage::View::OLD);
|
||||
ASSERT_TRUE(vertex);
|
||||
auto [edge_type, other_vertex, edge] =
|
||||
vertex->OutEdges({}, storage::View::NEW).GetReturn()[0];
|
||||
|
||||
ASSERT_TRUE(edge.GetProperty(5, storage::View::OLD).GetReturn().IsNull());
|
||||
ASSERT_TRUE(edge.GetProperty(5, storage::View::NEW).GetReturn().IsNull());
|
||||
ASSERT_EQ(edge.Properties(storage::View::OLD).GetReturn().size(), 0);
|
||||
ASSERT_EQ(edge.Properties(storage::View::NEW).GetReturn().size(), 0);
|
||||
|
||||
ASSERT_TRUE(edge.GetProperty(10, storage::View::OLD).GetReturn().IsNull());
|
||||
ASSERT_TRUE(edge.GetProperty(10, storage::View::NEW).GetReturn().IsNull());
|
||||
|
||||
acc.Abort();
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST(StorageV2, EdgePropertySerializationError) {
|
||||
storage::Storage store;
|
||||
storage::Gid gid =
|
||||
storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
|
||||
{
|
||||
auto acc = store.Access();
|
||||
auto vertex = acc.CreateVertex();
|
||||
gid = vertex.Gid();
|
||||
auto edge = acc.CreateEdge(&vertex, &vertex, 5).GetReturn();
|
||||
ASSERT_EQ(edge.EdgeType(), 5);
|
||||
ASSERT_EQ(edge.FromVertex(), vertex);
|
||||
ASSERT_EQ(edge.ToVertex(), vertex);
|
||||
acc.Commit();
|
||||
}
|
||||
|
||||
auto acc1 = store.Access();
|
||||
auto acc2 = store.Access();
|
||||
|
||||
// Set property 1 to 123 in accessor 1.
|
||||
{
|
||||
auto vertex = acc1.FindVertex(gid, storage::View::OLD);
|
||||
ASSERT_TRUE(vertex);
|
||||
auto [edge_type, other_vertex, edge] =
|
||||
vertex->OutEdges({}, storage::View::NEW).GetReturn()[0];
|
||||
|
||||
ASSERT_TRUE(edge.GetProperty(1, storage::View::OLD).GetReturn().IsNull());
|
||||
ASSERT_TRUE(edge.GetProperty(1, storage::View::NEW).GetReturn().IsNull());
|
||||
ASSERT_TRUE(edge.GetProperty(2, storage::View::OLD).GetReturn().IsNull());
|
||||
ASSERT_TRUE(edge.GetProperty(2, storage::View::NEW).GetReturn().IsNull());
|
||||
ASSERT_EQ(edge.Properties(storage::View::OLD).GetReturn().size(), 0);
|
||||
ASSERT_EQ(edge.Properties(storage::View::NEW).GetReturn().size(), 0);
|
||||
|
||||
{
|
||||
auto res = edge.SetProperty(1, storage::PropertyValue(123));
|
||||
ASSERT_TRUE(res.IsReturn());
|
||||
ASSERT_FALSE(res.GetReturn());
|
||||
}
|
||||
|
||||
ASSERT_TRUE(edge.GetProperty(1, storage::View::OLD).GetReturn().IsNull());
|
||||
ASSERT_EQ(edge.GetProperty(1, storage::View::NEW).GetReturn().ValueInt(),
|
||||
123);
|
||||
ASSERT_TRUE(edge.GetProperty(2, storage::View::OLD).GetReturn().IsNull());
|
||||
ASSERT_TRUE(edge.GetProperty(2, storage::View::NEW).GetReturn().IsNull());
|
||||
ASSERT_EQ(edge.Properties(storage::View::OLD).GetReturn().size(), 0);
|
||||
{
|
||||
auto properties = edge.Properties(storage::View::NEW).GetReturn();
|
||||
ASSERT_EQ(properties.size(), 1);
|
||||
ASSERT_EQ(properties[1].ValueInt(), 123);
|
||||
}
|
||||
}
|
||||
|
||||
// Set property 2 to "nandare" in accessor 2.
|
||||
{
|
||||
auto vertex = acc2.FindVertex(gid, storage::View::OLD);
|
||||
ASSERT_TRUE(vertex);
|
||||
auto [edge_type, other_vertex, edge] =
|
||||
vertex->OutEdges({}, storage::View::NEW).GetReturn()[0];
|
||||
|
||||
ASSERT_TRUE(edge.GetProperty(1, storage::View::OLD).GetReturn().IsNull());
|
||||
ASSERT_TRUE(edge.GetProperty(1, storage::View::NEW).GetReturn().IsNull());
|
||||
ASSERT_TRUE(edge.GetProperty(2, storage::View::OLD).GetReturn().IsNull());
|
||||
ASSERT_TRUE(edge.GetProperty(2, storage::View::NEW).GetReturn().IsNull());
|
||||
ASSERT_EQ(edge.Properties(storage::View::OLD).GetReturn().size(), 0);
|
||||
ASSERT_EQ(edge.Properties(storage::View::NEW).GetReturn().size(), 0);
|
||||
|
||||
{
|
||||
auto res = edge.SetProperty(2, storage::PropertyValue("nandare"));
|
||||
ASSERT_TRUE(res.IsError());
|
||||
ASSERT_EQ(res.GetError(), storage::Error::SERIALIZATION_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
// Finalize both accessors.
|
||||
acc1.Commit();
|
||||
acc2.Abort();
|
||||
|
||||
// Check which properties exist.
|
||||
{
|
||||
auto acc = store.Access();
|
||||
auto vertex = acc.FindVertex(gid, storage::View::OLD);
|
||||
ASSERT_TRUE(vertex);
|
||||
auto [edge_type, other_vertex, edge] =
|
||||
vertex->OutEdges({}, storage::View::NEW).GetReturn()[0];
|
||||
|
||||
ASSERT_EQ(edge.GetProperty(1, storage::View::OLD).GetReturn().ValueInt(),
|
||||
123);
|
||||
ASSERT_TRUE(edge.GetProperty(2, storage::View::OLD).GetReturn().IsNull());
|
||||
{
|
||||
auto properties = edge.Properties(storage::View::OLD).GetReturn();
|
||||
ASSERT_EQ(properties.size(), 1);
|
||||
ASSERT_EQ(properties[1].ValueInt(), 123);
|
||||
}
|
||||
|
||||
ASSERT_EQ(edge.GetProperty(1, storage::View::NEW).GetReturn().ValueInt(),
|
||||
123);
|
||||
ASSERT_TRUE(edge.GetProperty(2, storage::View::NEW).GetReturn().IsNull());
|
||||
{
|
||||
auto properties = edge.Properties(storage::View::NEW).GetReturn();
|
||||
ASSERT_EQ(properties.size(), 1);
|
||||
ASSERT_EQ(properties[1].ValueInt(), 123);
|
||||
}
|
||||
|
||||
acc.Abort();
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user