Add support for vertex properties to storage v2

Reviewers: mtomic, teon.banek

Reviewed By: mtomic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2163
This commit is contained in:
Matej Ferencevic 2019-07-02 13:46:43 +02:00
parent 542d65544b
commit db72ef05f8
7 changed files with 1122 additions and 22 deletions

View File

@ -2,6 +2,8 @@
#include <atomic>
#include "storage/v2/property_value.hpp"
namespace storage {
struct Delta {
@ -10,11 +12,13 @@ struct Delta {
RECREATE_OBJECT,
ADD_LABEL,
REMOVE_LABEL,
SET_PROPERTY,
};
Delta(Action action, uint64_t value, std::atomic<uint64_t> *timestamp,
uint64_t command_id)
Delta(Action action, uint64_t key, const PropertyValue &value,
std::atomic<uint64_t> *timestamp, uint64_t command_id)
: action(action),
key(key),
value(value),
timestamp(timestamp),
command_id(command_id),
@ -23,7 +27,8 @@ struct Delta {
Delta(Delta &&other) noexcept
: action(other.action),
value(other.value),
key(other.key),
value(std::move(other.value)),
timestamp(other.timestamp),
command_id(other.command_id),
prev(other.prev),
@ -36,7 +41,9 @@ struct Delta {
~Delta() {}
Action action;
uint64_t value;
uint64_t key; // Used as the label id or the property id.
PropertyValue value; // Used as the property value (only for SET_PROPERTY).
// TODO: optimize with in-place copy
std::atomic<uint64_t> *timestamp;

View File

@ -1,6 +1,7 @@
#pragma once
#include "storage/v2/delta.hpp"
#include "storage/v2/property_value.hpp"
#include "storage/v2/transaction.hpp"
#include "storage/v2/view.hpp"
@ -67,18 +68,21 @@ inline bool PrepareForWrite(Transaction *transaction, Vertex *vertex) {
/// the created delta. It doesn't perform any linking of the delta and is
/// primarily used to create the first delta for an object.
inline Delta *CreateDelta(Transaction *transaction, Delta::Action action,
uint64_t value) {
return &transaction->deltas.emplace_back(
action, value, &transaction->commit_timestamp, transaction->command_id);
uint64_t key) {
return &transaction->deltas.emplace_back(action, key, PropertyValue(),
&transaction->commit_timestamp,
transaction->command_id);
}
/// This function creates a delta in the transaction for the Vertex object and
/// links the delta into the Vertex's delta list. It also adds the Vertex to the
/// transaction's modified vertices list.
inline void CreateAndLinkDelta(Transaction *transaction, Vertex *vertex,
Delta::Action action, uint64_t value) {
auto delta = &transaction->deltas.emplace_back(
action, value, &transaction->commit_timestamp, transaction->command_id);
Delta::Action action, uint64_t key,
const PropertyValue &value = PropertyValue()) {
auto delta = &transaction->deltas.emplace_back(action, key, value,
&transaction->commit_timestamp,
transaction->command_id);
if (vertex->delta) {
vertex->delta->prev = delta;

View File

@ -128,7 +128,7 @@ class Storage final {
switch (current->action) {
case Delta::Action::REMOVE_LABEL: {
auto it = std::find(vertex->labels.begin(), vertex->labels.end(),
current->value);
current->key);
CHECK(it != vertex->labels.end()) << "Invalid database state!";
std::swap(*it, *vertex->labels.rbegin());
vertex->labels.pop_back();
@ -136,9 +136,24 @@ class Storage final {
}
case Delta::Action::ADD_LABEL: {
auto it = std::find(vertex->labels.begin(), vertex->labels.end(),
current->value);
current->key);
CHECK(it == vertex->labels.end()) << "Invalid database state!";
vertex->labels.push_back(current->value);
vertex->labels.push_back(current->key);
break;
}
case Delta::Action::SET_PROPERTY: {
auto it = vertex->properties.find(current->key);
if (it != vertex->properties.end()) {
if (current->value.IsNull()) {
// remove the property
vertex->properties.erase(it);
} else {
// set the value
it->second = current->value;
}
} else if (!current->value.IsNull()) {
vertex->properties.emplace(current->key, current->value);
}
break;
}
case Delta::Action::DELETE_OBJECT: {

View File

@ -7,6 +7,7 @@
#include "utils/skip_list.hpp"
#include "storage/v2/delta.hpp"
#include "storage/v2/property_value.hpp"
#include "storage/v2/vertex.hpp"
#include "storage/v2/view.hpp"

View File

@ -19,9 +19,7 @@ struct Vertex {
Gid gid;
std::vector<uint64_t> labels;
// TODO: add
// std::unordered_map<uint64_t, storage::PropertyValue> properties;
std::unordered_map<uint64_t, storage::PropertyValue> properties;
utils::SpinLock lock;
bool deleted;

View File

@ -33,6 +33,7 @@ class VertexAccessor final {
switch (delta.action) {
case Delta::Action::ADD_LABEL:
case Delta::Action::REMOVE_LABEL:
case Delta::Action::SET_PROPERTY:
break;
case Delta::Action::RECREATE_OBJECT: {
is_visible = true;
@ -116,14 +117,14 @@ class VertexAccessor final {
[&deleted, &has_label, label](const Delta &delta) {
switch (delta.action) {
case Delta::Action::REMOVE_LABEL: {
if (delta.value == label) {
if (delta.key == label) {
CHECK(has_label) << "Invalid database state!";
has_label = false;
}
break;
}
case Delta::Action::ADD_LABEL: {
if (delta.value == label) {
if (delta.key == label) {
CHECK(!has_label) << "Invalid database state!";
has_label = true;
}
@ -137,6 +138,8 @@ class VertexAccessor final {
deleted = false;
break;
}
case Delta::Action::SET_PROPERTY:
break;
}
});
if (deleted) return Result<bool>{Error::DELETED_OBJECT};
@ -158,7 +161,7 @@ class VertexAccessor final {
switch (delta.action) {
case Delta::Action::REMOVE_LABEL: {
// Remove the label because we don't see the addition.
auto it = std::find(labels.begin(), labels.end(), delta.value);
auto it = std::find(labels.begin(), labels.end(), delta.key);
CHECK(it != labels.end()) << "Invalid database state!";
std::swap(*it, *labels.rbegin());
labels.pop_back();
@ -166,9 +169,9 @@ class VertexAccessor final {
}
case Delta::Action::ADD_LABEL: {
// Add the label because we don't see the removal.
auto it = std::find(labels.begin(), labels.end(), delta.value);
auto it = std::find(labels.begin(), labels.end(), delta.key);
CHECK(it == labels.end()) << "Invalid database state!";
labels.push_back(delta.value);
labels.push_back(delta.key);
break;
}
case Delta::Action::DELETE_OBJECT: {
@ -179,12 +182,133 @@ class VertexAccessor final {
deleted = false;
break;
}
case Delta::Action::SET_PROPERTY:
break;
}
});
if (deleted) return Result<std::vector<uint64_t>>{Error::DELETED_OBJECT};
return Result<std::vector<uint64_t>>{std::move(labels)};
}
Result<bool> SetProperty(uint64_t property, const PropertyValue &value) {
std::lock_guard<utils::SpinLock> guard(vertex_->lock);
if (!PrepareForWrite(transaction_, vertex_))
return Result<bool>{Error::SERIALIZATION_ERROR};
if (vertex_->deleted) return Result<bool>{Error::DELETED_OBJECT};
auto it = vertex_->properties.find(property);
bool existed = it != vertex_->properties.end();
if (it != vertex_->properties.end()) {
CreateAndLinkDelta(transaction_, vertex_, Delta::Action::SET_PROPERTY,
property, it->second);
if (value.IsNull()) {
// remove the property
vertex_->properties.erase(it);
} else {
// set the value
it->second = value;
}
} else {
CreateAndLinkDelta(transaction_, vertex_, Delta::Action::SET_PROPERTY,
property, PropertyValue());
if (!value.IsNull()) {
vertex_->properties.emplace(property, value);
}
}
return Result<bool>{existed};
}
Result<PropertyValue> GetProperty(uint64_t property, View view) {
bool deleted = false;
PropertyValue value;
Delta *delta = nullptr;
{
std::lock_guard<utils::SpinLock> guard(vertex_->lock);
deleted = vertex_->deleted;
auto it = vertex_->properties.find(property);
if (it != vertex_->properties.end()) {
value = it->second;
}
delta = vertex_->delta;
}
ApplyDeltasForRead(transaction_, delta, view,
[&deleted, &value, property](const Delta &delta) {
switch (delta.action) {
case Delta::Action::SET_PROPERTY: {
if (delta.key == property) {
value = delta.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:
break;
}
});
if (deleted) return Result<PropertyValue>{Error::DELETED_OBJECT};
return Result<PropertyValue>{std::move(value)};
}
Result<std::unordered_map<uint64_t, PropertyValue>> Properties(View view) {
std::unordered_map<uint64_t, PropertyValue> properties;
bool deleted = false;
Delta *delta = nullptr;
{
std::lock_guard<utils::SpinLock> guard(vertex_->lock);
deleted = vertex_->deleted;
properties = vertex_->properties;
delta = vertex_->delta;
}
ApplyDeltasForRead(transaction_, delta, view,
[&deleted, &properties](const Delta &delta) {
switch (delta.action) {
case Delta::Action::SET_PROPERTY: {
auto it = properties.find(delta.key);
if (it != properties.end()) {
if (delta.value.IsNull()) {
// remove the property
properties.erase(it);
} else {
// set the value
it->second = delta.value;
}
} else if (!delta.value.IsNull()) {
properties.emplace(delta.key, delta.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:
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)};
}
Gid Gid() const { return vertex_->gid; }
private:

View File

@ -596,6 +596,169 @@ TEST(StorageV2, VertexDeleteLabel) {
}
}
// NOLINTNEXTLINE(hicpp-special-member-functions)
TEST(StorageV2, VertexDeleteProperty) {
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();
ASSERT_FALSE(acc.FindVertex(gid, storage::View::OLD).has_value());
ASSERT_TRUE(acc.FindVertex(gid, storage::View::NEW).has_value());
acc.Commit();
}
// Set property, delete the vertex and check the property API (same command)
{
auto acc = store.Access();
auto vertex = acc.FindVertex(gid, storage::View::NEW);
ASSERT_TRUE(vertex);
// Check whether property 5 exists
ASSERT_TRUE(
vertex->GetProperty(5, storage::View::OLD).GetReturn().IsNull());
ASSERT_TRUE(
vertex->GetProperty(5, storage::View::NEW).GetReturn().IsNull());
ASSERT_EQ(vertex->Properties(storage::View::OLD).GetReturn().size(), 0);
ASSERT_EQ(vertex->Properties(storage::View::NEW).GetReturn().size(), 0);
// Set property 5 to "nandare"
ASSERT_FALSE(
vertex->SetProperty(5, storage::PropertyValue("nandare")).GetReturn());
// Check whether property 5 exists
ASSERT_TRUE(
vertex->GetProperty(5, storage::View::OLD).GetReturn().IsNull());
ASSERT_EQ(
vertex->GetProperty(5, storage::View::NEW).GetReturn().ValueString(),
"nandare");
ASSERT_EQ(vertex->Properties(storage::View::OLD).GetReturn().size(), 0);
{
auto properties = vertex->Properties(storage::View::NEW).GetReturn();
ASSERT_EQ(properties.size(), 1);
ASSERT_EQ(properties[5].ValueString(), "nandare");
}
// Delete the vertex
ASSERT_TRUE(vertex->Delete().GetReturn());
// Check whether label 5 exists
ASSERT_TRUE(
vertex->GetProperty(5, storage::View::OLD).GetReturn().IsNull());
ASSERT_EQ(vertex->GetProperty(5, storage::View::NEW).GetError(),
storage::Error::DELETED_OBJECT);
ASSERT_EQ(vertex->Properties(storage::View::OLD).GetReturn().size(), 0);
ASSERT_EQ(vertex->Properties(storage::View::NEW).GetError(),
storage::Error::DELETED_OBJECT);
// Try to set the property
{
auto ret = vertex->SetProperty(5, storage::PropertyValue("haihai"));
ASSERT_TRUE(ret.IsError());
ASSERT_EQ(ret.GetError(), storage::Error::DELETED_OBJECT);
}
acc.Abort();
}
// Set property, delete the vertex and check the property API (different
// command)
{
auto acc = store.Access();
auto vertex = acc.FindVertex(gid, storage::View::NEW);
ASSERT_TRUE(vertex);
// Check whether property 5 exists
ASSERT_TRUE(
vertex->GetProperty(5, storage::View::OLD).GetReturn().IsNull());
ASSERT_TRUE(
vertex->GetProperty(5, storage::View::NEW).GetReturn().IsNull());
ASSERT_EQ(vertex->Properties(storage::View::OLD).GetReturn().size(), 0);
ASSERT_EQ(vertex->Properties(storage::View::NEW).GetReturn().size(), 0);
// Set property 5 to "nandare"
ASSERT_FALSE(
vertex->SetProperty(5, storage::PropertyValue("nandare")).GetReturn());
// Check whether property 5 exists
ASSERT_TRUE(
vertex->GetProperty(5, storage::View::OLD).GetReturn().IsNull());
ASSERT_EQ(
vertex->GetProperty(5, storage::View::NEW).GetReturn().ValueString(),
"nandare");
ASSERT_EQ(vertex->Properties(storage::View::OLD).GetReturn().size(), 0);
{
auto properties = vertex->Properties(storage::View::NEW).GetReturn();
ASSERT_EQ(properties.size(), 1);
ASSERT_EQ(properties[5].ValueString(), "nandare");
}
// Advance command
acc.AdvanceCommand();
// Check whether property 5 exists
ASSERT_EQ(
vertex->GetProperty(5, storage::View::OLD).GetReturn().ValueString(),
"nandare");
ASSERT_EQ(
vertex->GetProperty(5, storage::View::NEW).GetReturn().ValueString(),
"nandare");
{
auto properties = vertex->Properties(storage::View::OLD).GetReturn();
ASSERT_EQ(properties.size(), 1);
ASSERT_EQ(properties[5].ValueString(), "nandare");
}
{
auto properties = vertex->Properties(storage::View::NEW).GetReturn();
ASSERT_EQ(properties.size(), 1);
ASSERT_EQ(properties[5].ValueString(), "nandare");
}
// Delete the vertex
ASSERT_TRUE(vertex->Delete().GetReturn());
// Check whether property 5 exists
ASSERT_EQ(
vertex->GetProperty(5, storage::View::OLD).GetReturn().ValueString(),
"nandare");
ASSERT_EQ(vertex->GetProperty(5, storage::View::NEW).GetError(),
storage::Error::DELETED_OBJECT);
{
auto properties = vertex->Properties(storage::View::OLD).GetReturn();
ASSERT_EQ(properties.size(), 1);
ASSERT_EQ(properties[5].ValueString(), "nandare");
}
ASSERT_EQ(vertex->Properties(storage::View::NEW).GetError(),
storage::Error::DELETED_OBJECT);
// Advance command
acc.AdvanceCommand();
// Check whether property 5 exists
ASSERT_EQ(vertex->GetProperty(5, storage::View::OLD).GetError(),
storage::Error::DELETED_OBJECT);
ASSERT_EQ(vertex->GetProperty(5, storage::View::NEW).GetError(),
storage::Error::DELETED_OBJECT);
ASSERT_EQ(vertex->Properties(storage::View::OLD).GetError(),
storage::Error::DELETED_OBJECT);
ASSERT_EQ(vertex->Properties(storage::View::NEW).GetError(),
storage::Error::DELETED_OBJECT);
// Try to set the property
{
auto ret = vertex->SetProperty(5, storage::PropertyValue("haihai"));
ASSERT_TRUE(ret.IsError());
ASSERT_EQ(ret.GetError(), storage::Error::DELETED_OBJECT);
}
acc.Abort();
}
}
// NOLINTNEXTLINE(hicpp-special-member-functions)
TEST(StorageV2, VertexLabelCommit) {
storage::Storage store;
@ -940,7 +1103,7 @@ TEST(StorageV2, VertexLabelSerializationError) {
auto acc1 = store.Access();
auto acc2 = store.Access();
// Add label 10 in accessor 1.
// Add label 1 in accessor 1.
{
auto vertex = acc1.FindVertex(gid, storage::View::OLD);
ASSERT_TRUE(vertex);
@ -1024,3 +1187,791 @@ TEST(StorageV2, VertexLabelSerializationError) {
acc.Abort();
}
}
// NOLINTNEXTLINE(hicpp-special-member-functions)
TEST(StorageV2, VertexPropertyCommit) {
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();
ASSERT_TRUE(vertex.GetProperty(5, storage::View::NEW).GetReturn().IsNull());
ASSERT_EQ(vertex.Properties(storage::View::NEW).GetReturn().size(), 0);
{
auto res = vertex.SetProperty(5, storage::PropertyValue("temporary"));
ASSERT_TRUE(res.IsReturn());
ASSERT_FALSE(res.GetReturn());
}
ASSERT_EQ(
vertex.GetProperty(5, storage::View::NEW).GetReturn().ValueString(),
"temporary");
{
auto properties = vertex.Properties(storage::View::NEW).GetReturn();
ASSERT_EQ(properties.size(), 1);
ASSERT_EQ(properties[5].ValueString(), "temporary");
}
{
auto res = vertex.SetProperty(5, storage::PropertyValue("nandare"));
ASSERT_TRUE(res.IsReturn());
ASSERT_TRUE(res.GetReturn());
}
ASSERT_EQ(
vertex.GetProperty(5, storage::View::NEW).GetReturn().ValueString(),
"nandare");
{
auto properties = vertex.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);
ASSERT_EQ(
vertex->GetProperty(5, storage::View::OLD).GetReturn().ValueString(),
"nandare");
{
auto properties = vertex->Properties(storage::View::OLD).GetReturn();
ASSERT_EQ(properties.size(), 1);
ASSERT_EQ(properties[5].ValueString(), "nandare");
}
ASSERT_EQ(
vertex->GetProperty(5, storage::View::NEW).GetReturn().ValueString(),
"nandare");
{
auto properties = vertex->Properties(storage::View::NEW).GetReturn();
ASSERT_EQ(properties.size(), 1);
ASSERT_EQ(properties[5].ValueString(), "nandare");
}
ASSERT_TRUE(
vertex->GetProperty(10, storage::View::OLD).GetReturn().IsNull());
ASSERT_TRUE(
vertex->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 res = vertex->SetProperty(5, storage::PropertyValue());
ASSERT_TRUE(res.IsReturn());
ASSERT_TRUE(res.GetReturn());
}
ASSERT_EQ(
vertex->GetProperty(5, storage::View::OLD).GetReturn().ValueString(),
"nandare");
{
auto properties = vertex->Properties(storage::View::OLD).GetReturn();
ASSERT_EQ(properties.size(), 1);
ASSERT_EQ(properties[5].ValueString(), "nandare");
}
ASSERT_TRUE(
vertex->GetProperty(5, storage::View::NEW).GetReturn().IsNull());
ASSERT_EQ(vertex->Properties(storage::View::NEW).GetReturn().size(), 0);
{
auto res = vertex->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);
ASSERT_TRUE(
vertex->GetProperty(5, storage::View::OLD).GetReturn().IsNull());
ASSERT_TRUE(
vertex->GetProperty(5, storage::View::NEW).GetReturn().IsNull());
ASSERT_EQ(vertex->Properties(storage::View::OLD).GetReturn().size(), 0);
ASSERT_EQ(vertex->Properties(storage::View::NEW).GetReturn().size(), 0);
ASSERT_TRUE(
vertex->GetProperty(10, storage::View::OLD).GetReturn().IsNull());
ASSERT_TRUE(
vertex->GetProperty(10, storage::View::NEW).GetReturn().IsNull());
acc.Abort();
}
}
// NOLINTNEXTLINE(hicpp-special-member-functions)
TEST(StorageV2, VertexPropertyAbort) {
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();
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);
ASSERT_TRUE(
vertex->GetProperty(5, storage::View::NEW).GetReturn().IsNull());
ASSERT_EQ(vertex->Properties(storage::View::NEW).GetReturn().size(), 0);
{
auto res = vertex->SetProperty(5, storage::PropertyValue("temporary"));
ASSERT_TRUE(res.IsReturn());
ASSERT_FALSE(res.GetReturn());
}
ASSERT_EQ(
vertex->GetProperty(5, storage::View::NEW).GetReturn().ValueString(),
"temporary");
{
auto properties = vertex->Properties(storage::View::NEW).GetReturn();
ASSERT_EQ(properties.size(), 1);
ASSERT_EQ(properties[5].ValueString(), "temporary");
}
{
auto res = vertex->SetProperty(5, storage::PropertyValue("nandare"));
ASSERT_TRUE(res.IsReturn());
ASSERT_TRUE(res.GetReturn());
}
ASSERT_EQ(
vertex->GetProperty(5, storage::View::NEW).GetReturn().ValueString(),
"nandare");
{
auto properties = vertex->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);
ASSERT_TRUE(
vertex->GetProperty(5, storage::View::OLD).GetReturn().IsNull());
ASSERT_TRUE(
vertex->GetProperty(5, storage::View::NEW).GetReturn().IsNull());
ASSERT_EQ(vertex->Properties(storage::View::OLD).GetReturn().size(), 0);
ASSERT_EQ(vertex->Properties(storage::View::NEW).GetReturn().size(), 0);
ASSERT_TRUE(
vertex->GetProperty(10, storage::View::OLD).GetReturn().IsNull());
ASSERT_TRUE(
vertex->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);
ASSERT_TRUE(
vertex->GetProperty(5, storage::View::NEW).GetReturn().IsNull());
ASSERT_EQ(vertex->Properties(storage::View::NEW).GetReturn().size(), 0);
{
auto res = vertex->SetProperty(5, storage::PropertyValue("temporary"));
ASSERT_TRUE(res.IsReturn());
ASSERT_FALSE(res.GetReturn());
}
ASSERT_EQ(
vertex->GetProperty(5, storage::View::NEW).GetReturn().ValueString(),
"temporary");
{
auto properties = vertex->Properties(storage::View::NEW).GetReturn();
ASSERT_EQ(properties.size(), 1);
ASSERT_EQ(properties[5].ValueString(), "temporary");
}
{
auto res = vertex->SetProperty(5, storage::PropertyValue("nandare"));
ASSERT_TRUE(res.IsReturn());
ASSERT_TRUE(res.GetReturn());
}
ASSERT_EQ(
vertex->GetProperty(5, storage::View::NEW).GetReturn().ValueString(),
"nandare");
{
auto properties = vertex->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);
ASSERT_EQ(
vertex->GetProperty(5, storage::View::OLD).GetReturn().ValueString(),
"nandare");
{
auto properties = vertex->Properties(storage::View::OLD).GetReturn();
ASSERT_EQ(properties.size(), 1);
ASSERT_EQ(properties[5].ValueString(), "nandare");
}
ASSERT_EQ(
vertex->GetProperty(5, storage::View::NEW).GetReturn().ValueString(),
"nandare");
{
auto properties = vertex->Properties(storage::View::NEW).GetReturn();
ASSERT_EQ(properties.size(), 1);
ASSERT_EQ(properties[5].ValueString(), "nandare");
}
ASSERT_TRUE(
vertex->GetProperty(10, storage::View::OLD).GetReturn().IsNull());
ASSERT_TRUE(
vertex->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);
ASSERT_EQ(
vertex->GetProperty(5, storage::View::OLD).GetReturn().ValueString(),
"nandare");
{
auto properties = vertex->Properties(storage::View::OLD).GetReturn();
ASSERT_EQ(properties.size(), 1);
ASSERT_EQ(properties[5].ValueString(), "nandare");
}
ASSERT_EQ(
vertex->GetProperty(5, storage::View::NEW).GetReturn().ValueString(),
"nandare");
{
auto properties = vertex->Properties(storage::View::NEW).GetReturn();
ASSERT_EQ(properties.size(), 1);
ASSERT_EQ(properties[5].ValueString(), "nandare");
}
{
auto res = vertex->SetProperty(5, storage::PropertyValue());
ASSERT_TRUE(res.IsReturn());
ASSERT_TRUE(res.GetReturn());
}
ASSERT_EQ(
vertex->GetProperty(5, storage::View::OLD).GetReturn().ValueString(),
"nandare");
{
auto properties = vertex->Properties(storage::View::OLD).GetReturn();
ASSERT_EQ(properties.size(), 1);
ASSERT_EQ(properties[5].ValueString(), "nandare");
}
ASSERT_TRUE(
vertex->GetProperty(5, storage::View::NEW).GetReturn().IsNull());
ASSERT_EQ(vertex->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);
ASSERT_EQ(
vertex->GetProperty(5, storage::View::OLD).GetReturn().ValueString(),
"nandare");
{
auto properties = vertex->Properties(storage::View::OLD).GetReturn();
ASSERT_EQ(properties.size(), 1);
ASSERT_EQ(properties[5].ValueString(), "nandare");
}
ASSERT_EQ(
vertex->GetProperty(5, storage::View::NEW).GetReturn().ValueString(),
"nandare");
{
auto properties = vertex->Properties(storage::View::NEW).GetReturn();
ASSERT_EQ(properties.size(), 1);
ASSERT_EQ(properties[5].ValueString(), "nandare");
}
ASSERT_TRUE(
vertex->GetProperty(10, storage::View::OLD).GetReturn().IsNull());
ASSERT_TRUE(
vertex->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);
ASSERT_EQ(
vertex->GetProperty(5, storage::View::OLD).GetReturn().ValueString(),
"nandare");
{
auto properties = vertex->Properties(storage::View::OLD).GetReturn();
ASSERT_EQ(properties.size(), 1);
ASSERT_EQ(properties[5].ValueString(), "nandare");
}
ASSERT_EQ(
vertex->GetProperty(5, storage::View::NEW).GetReturn().ValueString(),
"nandare");
{
auto properties = vertex->Properties(storage::View::NEW).GetReturn();
ASSERT_EQ(properties.size(), 1);
ASSERT_EQ(properties[5].ValueString(), "nandare");
}
{
auto res = vertex->SetProperty(5, storage::PropertyValue());
ASSERT_TRUE(res.IsReturn());
ASSERT_TRUE(res.GetReturn());
}
ASSERT_EQ(
vertex->GetProperty(5, storage::View::OLD).GetReturn().ValueString(),
"nandare");
{
auto properties = vertex->Properties(storage::View::OLD).GetReturn();
ASSERT_EQ(properties.size(), 1);
ASSERT_EQ(properties[5].ValueString(), "nandare");
}
ASSERT_TRUE(
vertex->GetProperty(5, storage::View::NEW).GetReturn().IsNull());
ASSERT_EQ(vertex->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);
ASSERT_TRUE(
vertex->GetProperty(5, storage::View::OLD).GetReturn().IsNull());
ASSERT_TRUE(
vertex->GetProperty(5, storage::View::NEW).GetReturn().IsNull());
ASSERT_EQ(vertex->Properties(storage::View::OLD).GetReturn().size(), 0);
ASSERT_EQ(vertex->Properties(storage::View::NEW).GetReturn().size(), 0);
ASSERT_TRUE(
vertex->GetProperty(10, storage::View::OLD).GetReturn().IsNull());
ASSERT_TRUE(
vertex->GetProperty(10, storage::View::NEW).GetReturn().IsNull());
acc.Abort();
}
}
// NOLINTNEXTLINE(hicpp-special-member-functions)
TEST(StorageV2, VertexPropertySerializationError) {
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();
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);
ASSERT_TRUE(
vertex->GetProperty(1, storage::View::OLD).GetReturn().IsNull());
ASSERT_TRUE(
vertex->GetProperty(1, storage::View::NEW).GetReturn().IsNull());
ASSERT_TRUE(
vertex->GetProperty(2, storage::View::OLD).GetReturn().IsNull());
ASSERT_TRUE(
vertex->GetProperty(2, storage::View::NEW).GetReturn().IsNull());
ASSERT_EQ(vertex->Properties(storage::View::OLD).GetReturn().size(), 0);
ASSERT_EQ(vertex->Properties(storage::View::NEW).GetReturn().size(), 0);
{
auto res = vertex->SetProperty(1, storage::PropertyValue(123));
ASSERT_TRUE(res.IsReturn());
ASSERT_FALSE(res.GetReturn());
}
ASSERT_TRUE(
vertex->GetProperty(1, storage::View::OLD).GetReturn().IsNull());
ASSERT_EQ(vertex->GetProperty(1, storage::View::NEW).GetReturn().ValueInt(),
123);
ASSERT_TRUE(
vertex->GetProperty(2, storage::View::OLD).GetReturn().IsNull());
ASSERT_TRUE(
vertex->GetProperty(2, storage::View::NEW).GetReturn().IsNull());
ASSERT_EQ(vertex->Properties(storage::View::OLD).GetReturn().size(), 0);
{
auto properties = vertex->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);
ASSERT_TRUE(
vertex->GetProperty(1, storage::View::OLD).GetReturn().IsNull());
ASSERT_TRUE(
vertex->GetProperty(1, storage::View::NEW).GetReturn().IsNull());
ASSERT_TRUE(
vertex->GetProperty(2, storage::View::OLD).GetReturn().IsNull());
ASSERT_TRUE(
vertex->GetProperty(2, storage::View::NEW).GetReturn().IsNull());
ASSERT_EQ(vertex->Properties(storage::View::OLD).GetReturn().size(), 0);
ASSERT_EQ(vertex->Properties(storage::View::NEW).GetReturn().size(), 0);
{
auto res = vertex->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);
ASSERT_EQ(vertex->GetProperty(1, storage::View::OLD).GetReturn().ValueInt(),
123);
ASSERT_TRUE(
vertex->GetProperty(2, storage::View::OLD).GetReturn().IsNull());
{
auto properties = vertex->Properties(storage::View::OLD).GetReturn();
ASSERT_EQ(properties.size(), 1);
ASSERT_EQ(properties[1].ValueInt(), 123);
}
ASSERT_EQ(vertex->GetProperty(1, storage::View::NEW).GetReturn().ValueInt(),
123);
ASSERT_TRUE(
vertex->GetProperty(2, storage::View::NEW).GetReturn().IsNull());
{
auto properties = vertex->Properties(storage::View::NEW).GetReturn();
ASSERT_EQ(properties.size(), 1);
ASSERT_EQ(properties[1].ValueInt(), 123);
}
acc.Abort();
}
}
// NOLINTNEXTLINE(hicpp-special-member-functions)
TEST(StorageV2, VertexLabelPropertyMixed) {
storage::Storage store;
auto acc = store.Access();
auto vertex = acc.CreateVertex();
// Check whether label 5 and property 5 exist
ASSERT_FALSE(vertex.HasLabel(5, storage::View::NEW).GetReturn());
ASSERT_EQ(vertex.Labels(storage::View::NEW).GetReturn().size(), 0);
ASSERT_TRUE(vertex.GetProperty(5, storage::View::NEW).GetReturn().IsNull());
ASSERT_EQ(vertex.Properties(storage::View::NEW).GetReturn().size(), 0);
// Add label 5
ASSERT_TRUE(vertex.AddLabel(5).GetReturn());
// Check whether label 5 and property 5 exist
ASSERT_TRUE(vertex.HasLabel(5, storage::View::NEW).GetReturn());
{
auto labels = vertex.Labels(storage::View::NEW).GetReturn();
ASSERT_EQ(labels.size(), 1);
ASSERT_EQ(labels[0], 5);
}
ASSERT_TRUE(vertex.GetProperty(5, storage::View::NEW).GetReturn().IsNull());
ASSERT_EQ(vertex.Properties(storage::View::NEW).GetReturn().size(), 0);
// Advance command
acc.AdvanceCommand();
// Check whether label 5 and property 5 exist
ASSERT_TRUE(vertex.HasLabel(5, storage::View::OLD).GetReturn());
ASSERT_TRUE(vertex.HasLabel(5, storage::View::NEW).GetReturn());
{
auto labels = vertex.Labels(storage::View::OLD).GetReturn();
ASSERT_EQ(labels.size(), 1);
ASSERT_EQ(labels[0], 5);
}
{
auto labels = vertex.Labels(storage::View::NEW).GetReturn();
ASSERT_EQ(labels.size(), 1);
ASSERT_EQ(labels[0], 5);
}
ASSERT_TRUE(vertex.GetProperty(5, storage::View::OLD).GetReturn().IsNull());
ASSERT_TRUE(vertex.GetProperty(5, storage::View::NEW).GetReturn().IsNull());
ASSERT_EQ(vertex.Properties(storage::View::OLD).GetReturn().size(), 0);
ASSERT_EQ(vertex.Properties(storage::View::NEW).GetReturn().size(), 0);
// Set property 5 to "nandare"
ASSERT_FALSE(
vertex.SetProperty(5, storage::PropertyValue("nandare")).GetReturn());
// Check whether label 5 and property 5 exist
ASSERT_TRUE(vertex.HasLabel(5, storage::View::OLD).GetReturn());
ASSERT_TRUE(vertex.HasLabel(5, storage::View::NEW).GetReturn());
{
auto labels = vertex.Labels(storage::View::OLD).GetReturn();
ASSERT_EQ(labels.size(), 1);
ASSERT_EQ(labels[0], 5);
}
{
auto labels = vertex.Labels(storage::View::NEW).GetReturn();
ASSERT_EQ(labels.size(), 1);
ASSERT_EQ(labels[0], 5);
}
ASSERT_TRUE(vertex.GetProperty(5, storage::View::OLD).GetReturn().IsNull());
ASSERT_EQ(vertex.GetProperty(5, storage::View::NEW).GetReturn().ValueString(),
"nandare");
ASSERT_EQ(vertex.Properties(storage::View::OLD).GetReturn().size(), 0);
{
auto properties = vertex.Properties(storage::View::NEW).GetReturn();
ASSERT_EQ(properties.size(), 1);
ASSERT_EQ(properties[5].ValueString(), "nandare");
}
// Advance command
acc.AdvanceCommand();
// Check whether label 5 and property 5 exist
ASSERT_TRUE(vertex.HasLabel(5, storage::View::OLD).GetReturn());
ASSERT_TRUE(vertex.HasLabel(5, storage::View::NEW).GetReturn());
{
auto labels = vertex.Labels(storage::View::OLD).GetReturn();
ASSERT_EQ(labels.size(), 1);
ASSERT_EQ(labels[0], 5);
}
{
auto labels = vertex.Labels(storage::View::NEW).GetReturn();
ASSERT_EQ(labels.size(), 1);
ASSERT_EQ(labels[0], 5);
}
ASSERT_EQ(vertex.GetProperty(5, storage::View::OLD).GetReturn().ValueString(),
"nandare");
ASSERT_EQ(vertex.GetProperty(5, storage::View::NEW).GetReturn().ValueString(),
"nandare");
{
auto properties = vertex.Properties(storage::View::OLD).GetReturn();
ASSERT_EQ(properties.size(), 1);
ASSERT_EQ(properties[5].ValueString(), "nandare");
}
{
auto properties = vertex.Properties(storage::View::NEW).GetReturn();
ASSERT_EQ(properties.size(), 1);
ASSERT_EQ(properties[5].ValueString(), "nandare");
}
// Set property 5 to "haihai"
ASSERT_TRUE(
vertex.SetProperty(5, storage::PropertyValue("haihai")).GetReturn());
// Check whether label 5 and property 5 exist
ASSERT_TRUE(vertex.HasLabel(5, storage::View::OLD).GetReturn());
ASSERT_TRUE(vertex.HasLabel(5, storage::View::NEW).GetReturn());
{
auto labels = vertex.Labels(storage::View::OLD).GetReturn();
ASSERT_EQ(labels.size(), 1);
ASSERT_EQ(labels[0], 5);
}
{
auto labels = vertex.Labels(storage::View::NEW).GetReturn();
ASSERT_EQ(labels.size(), 1);
ASSERT_EQ(labels[0], 5);
}
ASSERT_EQ(vertex.GetProperty(5, storage::View::OLD).GetReturn().ValueString(),
"nandare");
ASSERT_EQ(vertex.GetProperty(5, storage::View::NEW).GetReturn().ValueString(),
"haihai");
{
auto properties = vertex.Properties(storage::View::OLD).GetReturn();
ASSERT_EQ(properties.size(), 1);
ASSERT_EQ(properties[5].ValueString(), "nandare");
}
{
auto properties = vertex.Properties(storage::View::NEW).GetReturn();
ASSERT_EQ(properties.size(), 1);
ASSERT_EQ(properties[5].ValueString(), "haihai");
}
// Advance command
acc.AdvanceCommand();
// Check whether label 5 and property 5 exist
ASSERT_TRUE(vertex.HasLabel(5, storage::View::OLD).GetReturn());
ASSERT_TRUE(vertex.HasLabel(5, storage::View::NEW).GetReturn());
{
auto labels = vertex.Labels(storage::View::OLD).GetReturn();
ASSERT_EQ(labels.size(), 1);
ASSERT_EQ(labels[0], 5);
}
{
auto labels = vertex.Labels(storage::View::NEW).GetReturn();
ASSERT_EQ(labels.size(), 1);
ASSERT_EQ(labels[0], 5);
}
ASSERT_EQ(vertex.GetProperty(5, storage::View::OLD).GetReturn().ValueString(),
"haihai");
ASSERT_EQ(vertex.GetProperty(5, storage::View::NEW).GetReturn().ValueString(),
"haihai");
{
auto properties = vertex.Properties(storage::View::OLD).GetReturn();
ASSERT_EQ(properties.size(), 1);
ASSERT_EQ(properties[5].ValueString(), "haihai");
}
{
auto properties = vertex.Properties(storage::View::NEW).GetReturn();
ASSERT_EQ(properties.size(), 1);
ASSERT_EQ(properties[5].ValueString(), "haihai");
}
// Remove label 5
ASSERT_TRUE(vertex.RemoveLabel(5).GetReturn());
// Check whether label 5 and property 5 exist
ASSERT_TRUE(vertex.HasLabel(5, storage::View::OLD).GetReturn());
ASSERT_FALSE(vertex.HasLabel(5, storage::View::NEW).GetReturn());
{
auto labels = vertex.Labels(storage::View::OLD).GetReturn();
ASSERT_EQ(labels.size(), 1);
ASSERT_EQ(labels[0], 5);
}
ASSERT_EQ(vertex.Labels(storage::View::NEW).GetReturn().size(), 0);
ASSERT_EQ(vertex.GetProperty(5, storage::View::OLD).GetReturn().ValueString(),
"haihai");
ASSERT_EQ(vertex.GetProperty(5, storage::View::NEW).GetReturn().ValueString(),
"haihai");
{
auto properties = vertex.Properties(storage::View::OLD).GetReturn();
ASSERT_EQ(properties.size(), 1);
ASSERT_EQ(properties[5].ValueString(), "haihai");
}
{
auto properties = vertex.Properties(storage::View::NEW).GetReturn();
ASSERT_EQ(properties.size(), 1);
ASSERT_EQ(properties[5].ValueString(), "haihai");
}
// Advance command
acc.AdvanceCommand();
// Check whether label 5 and property 5 exist
ASSERT_FALSE(vertex.HasLabel(5, storage::View::OLD).GetReturn());
ASSERT_FALSE(vertex.HasLabel(5, storage::View::NEW).GetReturn());
ASSERT_EQ(vertex.Labels(storage::View::OLD).GetReturn().size(), 0);
ASSERT_EQ(vertex.Labels(storage::View::NEW).GetReturn().size(), 0);
ASSERT_EQ(vertex.GetProperty(5, storage::View::OLD).GetReturn().ValueString(),
"haihai");
ASSERT_EQ(vertex.GetProperty(5, storage::View::NEW).GetReturn().ValueString(),
"haihai");
{
auto properties = vertex.Properties(storage::View::OLD).GetReturn();
ASSERT_EQ(properties.size(), 1);
ASSERT_EQ(properties[5].ValueString(), "haihai");
}
{
auto properties = vertex.Properties(storage::View::NEW).GetReturn();
ASSERT_EQ(properties.size(), 1);
ASSERT_EQ(properties[5].ValueString(), "haihai");
}
// Set property 5 to null
ASSERT_TRUE(vertex.SetProperty(5, storage::PropertyValue()).GetReturn());
// Check whether label 5 and property 5 exist
ASSERT_FALSE(vertex.HasLabel(5, storage::View::OLD).GetReturn());
ASSERT_FALSE(vertex.HasLabel(5, storage::View::NEW).GetReturn());
ASSERT_EQ(vertex.Labels(storage::View::OLD).GetReturn().size(), 0);
ASSERT_EQ(vertex.Labels(storage::View::NEW).GetReturn().size(), 0);
ASSERT_EQ(vertex.GetProperty(5, storage::View::OLD).GetReturn().ValueString(),
"haihai");
ASSERT_TRUE(vertex.GetProperty(5, storage::View::NEW).GetReturn().IsNull());
{
auto properties = vertex.Properties(storage::View::OLD).GetReturn();
ASSERT_EQ(properties.size(), 1);
ASSERT_EQ(properties[5].ValueString(), "haihai");
}
ASSERT_EQ(vertex.Properties(storage::View::NEW).GetReturn().size(), 0);
// Advance command
acc.AdvanceCommand();
// Check whether label 5 and property 5 exist
ASSERT_FALSE(vertex.HasLabel(5, storage::View::OLD).GetReturn());
ASSERT_FALSE(vertex.HasLabel(5, storage::View::NEW).GetReturn());
ASSERT_EQ(vertex.Labels(storage::View::OLD).GetReturn().size(), 0);
ASSERT_EQ(vertex.Labels(storage::View::NEW).GetReturn().size(), 0);
ASSERT_TRUE(vertex.GetProperty(5, storage::View::NEW).GetReturn().IsNull());
ASSERT_TRUE(vertex.GetProperty(5, storage::View::NEW).GetReturn().IsNull());
ASSERT_EQ(vertex.Properties(storage::View::OLD).GetReturn().size(), 0);
ASSERT_EQ(vertex.Properties(storage::View::NEW).GetReturn().size(), 0);
acc.Commit();
}