UniqueLabelPropertiesConstraint implementation and tests

Summary:
Same as `UniqueLabelPropertyConstratint` except it works with multiple properties.
Because of that it is a bit more complex and slower.

Reviewers: msantl, ipaljak

Reviewed By: msantl

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D1926
This commit is contained in:
Vinko Kasljevic 2019-04-08 09:14:04 +02:00
parent e1ad5cd803
commit 47a1e302a0
8 changed files with 728 additions and 16 deletions

View File

@ -53,6 +53,7 @@ set(mg_single_node_sources
storage/single_node/vertex_accessor.cpp
storage/single_node/constraints/existence_constraints.cpp
storage/single_node/constraints/record.cpp
storage/single_node/constraints/unique_label_properties_constraint.cpp
storage/single_node/constraints/unique_label_property_constraint.cpp
transactions/single_node/engine.cpp
memgraph_init.cpp

View File

@ -0,0 +1,28 @@
/// @file
#pragma once
#include "storage/common/types/property_value_store.hpp"
#include "transactions/single_node/engine.hpp"
#include "transactions/snapshot.hpp"
namespace storage::constraints::common {
template <typename TConstraints>
void UniqueConstraintRefresh(const tx::Snapshot &snapshot,
const tx::Engine &engine,
TConstraints &constraints, std::mutex &lock) {
std::lock_guard<std::mutex> guard(lock);
for (auto &constraint : constraints) {
for (auto p = constraint.version_pairs.begin();
p != constraint.version_pairs.end(); ++p) {
auto exp_id = p->record.tx_id_exp;
auto cre_id = p->record.tx_id_cre;
if ((exp_id != 0 && exp_id < snapshot.back() &&
engine.Info(exp_id).is_committed() && !snapshot.contains(exp_id)) ||
(cre_id < snapshot.back() && engine.Info(cre_id).is_aborted())) {
constraint.version_pairs.erase(p);
}
}
}
}
} // namespace storage::constraints::common

View File

@ -1,9 +1,11 @@
#include "storage/single_node/constraints/existence_constraints.hpp"
#include "storage/single_node/constraints/common.hpp"
namespace storage::constraints {
bool Contains(const PropertyValueStore &store,
const std::vector<storage::Property> &properties) {
for (auto &property : properties) {
for (auto property : properties) {
if (store.at(property).IsNull()) {
return false;
}

View File

@ -0,0 +1,155 @@
#include "storage/single_node/constraints/unique_label_properties_constraint.hpp"
#include <algorithm>
#include "storage/single_node/constraints/common.hpp"
#include "storage/single_node/vertex_accessor.hpp"
namespace storage::constraints {
auto FindIn(storage::Label label,
const std::vector<storage::Property> &properties,
const std::list<impl::LabelPropertiesEntry> &constraints) {
return std::find_if(
constraints.begin(), constraints.end(), [label, properties](auto &c) {
return c.label == label &&
std::is_permutation(properties.begin(), properties.end(),
c.properties.begin(), c.properties.end());
});
}
auto FindEntry(std::list<impl::LabelPropertyPair> &version_pairs,
const std::vector<PropertyValue> &values) {
return std::find_if(version_pairs.begin(), version_pairs.end(),
[values](auto &p) { return p.values == values; });
}
void UniqueLabelPropertiesConstraint::AddConstraint(
storage::Label label, const std::vector<storage::Property> &properties,
const tx::Transaction &t) {
auto constraint = FindIn(label, properties, constraints_);
if (constraint == constraints_.end())
constraints_.emplace_back(label, properties);
}
void UniqueLabelPropertiesConstraint::RemoveConstraint(
storage::Label label, const std::vector<storage::Property> &properties) {
auto constraint = FindIn(label, properties, constraints_);
if (constraint != constraints_.end()) constraints_.erase(constraint);
}
bool UniqueLabelPropertiesConstraint::Exists(
storage::Label label,
const std::vector<storage::Property> &properties) const {
return FindIn(label, properties, constraints_) != constraints_.end();
}
std::vector<LabelProperties> UniqueLabelPropertiesConstraint::ListConstraints()
const {
std::vector<LabelProperties> constraints;
constraints.reserve(constraints_.size());
std::transform(constraints_.begin(), constraints_.end(),
std::back_inserter(constraints), [](auto &c) {
return LabelProperties{c.label, c.properties};
});
return constraints;
}
void UniqueLabelPropertiesConstraint::UpdateOnAddLabel(
storage::Label label, const VertexAccessor &accessor,
const tx::Transaction &t) {
auto &vertex = accessor.current();
std::lock_guard<std::mutex> guard(lock_);
for (auto &constraint : constraints_) {
if (constraint.label != label) continue;
std::vector<PropertyValue> values;
for (auto p : constraint.properties) {
auto value = vertex.properties_.at(p);
if (value.IsNull()) break;
values.emplace_back(value);
}
if (values.size() != constraint.properties.size()) continue;
auto entry = FindEntry(constraint.version_pairs, values);
if (entry != constraint.version_pairs.end()) {
entry->record.Insert(accessor.gid(), t);
} else {
constraint.version_pairs.emplace_back(accessor.gid(), values, t);
}
}
}
void UniqueLabelPropertiesConstraint::UpdateOnRemoveLabel(
storage::Label label, const VertexAccessor &accessor,
const tx::Transaction &t) {
auto &vertex = accessor.current();
std::lock_guard<std::mutex> guard(lock_);
for (auto &constraint : constraints_) {
if (constraint.label != label) continue;
std::vector<PropertyValue> values;
for (auto p : constraint.properties) {
auto value = vertex.properties_.at(p);
if (value.IsNull()) break;
values.emplace_back(value);
}
if (values.size() != constraint.properties.size()) continue;
auto entry = FindEntry(constraint.version_pairs, values);
if (entry != constraint.version_pairs.end())
entry->record.Remove(accessor.gid(), t);
}
}
void UniqueLabelPropertiesConstraint::UpdateOnAddProperty(
storage::Property property, const PropertyValue &value,
const VertexAccessor &accessor, const tx::Transaction &t) {
auto &vertex = accessor.current();
std::lock_guard<std::mutex> guard(lock_);
for (auto &constraint : constraints_) {
if (!utils::Contains(constraint.properties, property)) continue;
if (!utils::Contains(vertex.labels_, constraint.label)) continue;
std::vector<PropertyValue> values;
for (auto p : constraint.properties) {
auto value = vertex.properties_.at(p);
if (value.IsNull()) break;
values.emplace_back(value);
}
if (values.size() != constraint.properties.size()) continue;
auto entry = FindEntry(constraint.version_pairs, values);
if (entry != constraint.version_pairs.end()) {
entry->record.Insert(accessor.gid(), t);
} else {
constraint.version_pairs.emplace_back(accessor.gid(), values, t);
}
}
}
void UniqueLabelPropertiesConstraint::UpdateOnRemoveProperty(
storage::Property property, const PropertyValue &value,
const VertexAccessor &accessor, const tx::Transaction &t) {
auto &vertex = accessor.current();
std::lock_guard<std::mutex> guard(lock_);
for (auto &constraint : constraints_) {
if (!utils::Contains(constraint.properties, property)) continue;
if (!utils::Contains(vertex.labels_, constraint.label)) continue;
std::vector<PropertyValue> values;
for (auto p : constraint.properties) {
if (p == property) {
values.emplace_back(value);
} else {
auto v = vertex.properties_.at(p);
if (v.IsNull()) break;
values.emplace_back(v);
}
}
if (values.size() != constraint.properties.size()) continue;
auto entry = FindEntry(constraint.version_pairs, values);
if (entry != constraint.version_pairs.end())
entry->record.Remove(accessor.gid(), t);
}
}
void UniqueLabelPropertiesConstraint::Refresh(const tx::Snapshot &snapshot,
const tx::Engine &engine) {
common::UniqueConstraintRefresh(snapshot, engine, constraints_, lock_);
}
} // namespace storage::constraints

View File

@ -0,0 +1,143 @@
/// @file
#pragma once
#include <list>
#include <mutex>
#include "storage/common/types/property_value.hpp"
#include "storage/common/types/types.hpp"
#include "storage/single_node/constraints/record.hpp"
namespace tx {
class Snapshot;
}; // namespace tx
class VertexAccessor;
namespace storage::constraints {
namespace impl {
struct LabelPropertyPair {
LabelPropertyPair(gid::Gid gid, const std::vector<PropertyValue> &v,
const tx::Transaction &t)
: values(v), record(gid, t) {}
std::vector<PropertyValue> values;
Record record;
};
struct LabelPropertiesEntry {
LabelPropertiesEntry(storage::Label l, const std::vector<storage::Property> &p)
: label(l), properties(p) {}
storage::Label label;
std::vector<storage::Property> properties;
std::list<LabelPropertyPair> version_pairs;
};
} // namespace impl
struct LabelProperties {
// This struct is used by ListConstraints method in order to avoid using
// std::pair or something like that.
storage::Label label;
std::vector<storage::Property> properties;
};
/// UniqueLabelPropertiesConstraint contains all unique constraints defined by
/// both label and set of properties. To create or delete unique constraint, caller
/// must ensure that there are no other transactions running in parallel.
/// Additionally, for adding unique constraint caller must first call
/// AddConstraint to create unique constraint and then call UpdateOnAddLabel or
/// UpdateOnAddProperty for every existing Vertex. If there is a unique
/// constraint violation then caller must manually handle that by catching
/// exception and calling RemoveConstraint method. This is needed to ensure
/// logical correctness of transactions. Once created, client uses UpdateOn
/// methods to notify UniqueLabelPropertyConstraint about changes. In case of
/// violation UpdateOn methods throw IndexConstraintViolationException
/// exception. Methods can also throw SerializationError. This class is thread
/// safe.
class UniqueLabelPropertiesConstraint {
public:
UniqueLabelPropertiesConstraint() = default;
UniqueLabelPropertiesConstraint(const UniqueLabelPropertiesConstraint &) =
delete;
UniqueLabelPropertiesConstraint(UniqueLabelPropertiesConstraint &&) = delete;
UniqueLabelPropertiesConstraint &operator=(
const UniqueLabelPropertiesConstraint &) = delete;
UniqueLabelPropertiesConstraint &operator=(
UniqueLabelPropertiesConstraint &&) = delete;
/// Add new unique constraint, if constraint already exists this method does
/// nothing. This method doesn't check if any of the existing vertices breaks
/// this constraint. Caller must do that instead. Caller must also ensure that
/// no other transaction is running in parallel.
void AddConstraint(storage::Label label,
const std::vector<storage::Property> &properties,
const tx::Transaction &t);
/// Removes existing unique constraint, if the constraint doesn't exist this
/// method does nothing. Caller must ensure that no other transaction is
/// running in parallel.
void RemoveConstraint(storage::Label label,
const std::vector<storage::Property> &properties);
/// Checks whether given unique constraint is visible.
bool Exists(storage::Label label,
const std::vector<storage::Property> &properties) const;
/// Returns list of unique constraints.
std::vector<LabelProperties> ListConstraints() const;
/// Updates unique constraint versions when adding label.
/// @param label - label that was added
/// @param accessor - accessor that was updated
/// @param t - current transaction
///
/// @throws IndexConstraintViolationException
/// @throws SerializationError
void UpdateOnAddLabel(storage::Label label, const VertexAccessor &accessor,
const tx::Transaction &t);
/// Updates unique constraint versions when removing label.
/// @param label - label that was removed
/// @param accessor - accessor that was updated
/// @param t - current transaction
///
/// @throws SerializationError
void UpdateOnRemoveLabel(storage::Label label, const VertexAccessor &accessor,
const tx::Transaction &t);
/// Updates unique constraint versions when adding property.
/// @param property - property that was added
/// @param value - property value that was added
/// @param accessor - accessor that was updated
/// @param t - current transaction
///
/// @throws IndexConstraintViolationException
/// @throws SerializationError
void UpdateOnAddProperty(storage::Property property,
const PropertyValue &value,
const VertexAccessor &accessor,
const tx::Transaction &t);
/// Updates unique constraint versions when removing property.
/// @param property - property that was removed
/// @param value - property value that was removed
/// @param accessor - accessor that was updated
/// @param t - current transaction
///
/// @throws SerializationError
void UpdateOnRemoveProperty(storage::Property property,
const PropertyValue &value,
const VertexAccessor &accessor,
const tx::Transaction &t);
/// Removes records that are no longer visible.
void Refresh(const tx::Snapshot &snapshot, const tx::Engine &engine);
private:
std::mutex lock_;
std::list<impl::LabelPropertiesEntry> constraints_;
};
} // namespace storage::constraints

View File

@ -1,7 +1,7 @@
#include "storage/single_node/constraints/unique_label_property_constraint.hpp"
#include "storage/single_node/record_accessor.hpp"
#include "storage/single_node/vertex.hpp"
#include "storage/single_node/constraints/common.hpp"
#include "storage/single_node/vertex_accessor.hpp"
#include "utils/algorithm.hpp"
namespace storage::constraints {
@ -162,18 +162,22 @@ void UniqueLabelPropertyConstraint::UpdateOnRemoveProperty(
void UniqueLabelPropertyConstraint::Refresh(const tx::Snapshot &snapshot,
const tx::Engine &engine) {
std::lock_guard<std::mutex> guard(lock_);
for (auto &constraint : constraints_) {
for (auto p = constraint.version_pairs.begin();
p != constraint.version_pairs.end(); ++p) {
auto exp_id = p->record.tx_id_exp;
auto cre_id = p->record.tx_id_cre;
if ((exp_id != 0 && exp_id < snapshot.back() &&
engine.Info(exp_id).is_committed() && !snapshot.contains(exp_id)) ||
(cre_id < snapshot.back() && engine.Info(cre_id).is_aborted())) {
constraint.version_pairs.erase(p);
}
}
}
// <<<<<<< HEAD
// std::lock_guard<std::mutex> guard(lock_);
// for (auto &constraint : constraints_) {
// for (auto p = constraint.version_pairs.begin();
// p != constraint.version_pairs.end(); ++p) {
// auto exp_id = p->record.tx_id_exp;
// auto cre_id = p->record.tx_id_cre;
// if ((exp_id != 0 && exp_id < snapshot.back() &&
// engine.Info(exp_id).is_committed() && !snapshot.contains(exp_id)) ||
// (cre_id < snapshot.back() && engine.Info(cre_id).is_aborted())) {
// constraint.version_pairs.erase(p);
// }
// }
// }
// =======
common::UniqueConstraintRefresh(snapshot, engine, constraints_, lock_);
// >>>>>>> Apply requested changes
}
} // namespace storage::constraints

View File

@ -288,6 +288,9 @@ target_link_libraries(${test_prefix}typed_value mg-single-node kvstore_dummy_lib
add_unit_test(unique_label_property_constraint.cpp)
target_link_libraries(${test_prefix}unique_label_property_constraint mg-single-node kvstore_dummy_lib)
add_unit_test(unique_label_properties_constraint.cpp)
target_link_libraries(${test_prefix}unique_label_properties_constraint mg-single-node kvstore_dummy_lib)
# Test mg-communication
add_unit_test(bolt_chunked_decoder_buffer.cpp)

View File

@ -0,0 +1,376 @@
#include <gflags/gflags.h>
#include <glog/logging.h>
#include <gtest/gtest.h>
#include "database/single_node/graph_db.hpp"
#include "database/single_node/graph_db_accessor.hpp"
#include "storage/single_node/constraints/unique_label_properties_constraint.hpp"
class UniqueLabelPropertiesTest : public ::testing::Test {
public:
void SetUp() override {
auto dba = db_.AccessBlocking();
label_ = dba->Label("label");
property1_ = dba->Property("property1");
property2_ = dba->Property("property2");
property3_ = dba->Property("property3");
constraint_.AddConstraint(label_, {property1_, property2_, property3_},
dba->transaction());
dba->Commit();
}
database::GraphDb db_;
storage::Label label_;
storage::Property property1_;
storage::Property property2_;
storage::Property property3_;
PropertyValue value1_{"value1"};
PropertyValue value2_{"value2"};
PropertyValue value3_{"value3"};
storage::constraints::UniqueLabelPropertiesConstraint constraint_;
};
TEST_F(UniqueLabelPropertiesTest, BuildDrop) {
{
auto dba = db_.Access();
EXPECT_TRUE(
constraint_.Exists(label_, {property2_, property1_, property3_}));
EXPECT_TRUE(
constraint_.Exists(label_, {property1_, property2_, property3_}));
EXPECT_FALSE(
constraint_.Exists(label_, {property1_, property2_}));
dba->Commit();
}
{
auto dba = db_.AccessBlocking();
constraint_.RemoveConstraint(label_, {property2_, property1_, property3_});
dba->Commit();
}
{
auto dba = db_.Access();
EXPECT_FALSE(
constraint_.Exists(label_, {property2_, property1_, property3_}));
EXPECT_FALSE(
constraint_.Exists(label_, {property1_, property2_, property3_}));
dba->Commit();
}
}
TEST_F(UniqueLabelPropertiesTest, BuildWithViolation) {
auto dba1 = db_.Access();
auto v1 = dba1->InsertVertex();
v1.add_label(label_);
v1.PropsSet(property1_, value1_);
v1.PropsSet(property2_, value2_);
v1.PropsSet(property3_, value3_);
auto v2 = dba1->InsertVertex();
v2.add_label(label_);
v2.PropsSet(property1_, value1_);
v2.PropsSet(property3_, value3_);
auto v3 = dba1->InsertVertex();
v3.add_label(label_);
v3.PropsSet(property3_, value3_);
v3.PropsSet(property1_, value1_);
v3.PropsSet(property2_, value2_);
dba1->Commit();
auto dba2 = db_.Access();
auto v4 = dba2->FindVertex(v1.gid(), false);
auto v5 = dba2->FindVertex(v2.gid(), false);
auto v6 = dba2->FindVertex(v3.gid(), false);
constraint_.UpdateOnAddLabel(label_, v4, dba2->transaction());
constraint_.UpdateOnAddLabel(label_, v5, dba2->transaction());
EXPECT_THROW(constraint_.UpdateOnAddLabel(label_, v6, dba2->transaction()),
database::IndexConstraintViolationException);
}
TEST_F(UniqueLabelPropertiesTest, InsertInsert) {
{
auto dba = db_.Access();
auto v = dba->InsertVertex();
v.add_label(label_);
constraint_.UpdateOnAddLabel(label_, v, dba->transaction());
v.PropsSet(property2_, value2_);
constraint_.UpdateOnAddProperty(property2_, value2_, v, dba->transaction());
v.PropsSet(property1_, value1_);
constraint_.UpdateOnAddProperty(property1_, value1_, v, dba->transaction());
v.PropsSet(property3_, value3_);
constraint_.UpdateOnAddProperty(property3_, value3_, v, dba->transaction());
dba->Commit();
}
{
auto dba = db_.Access();
auto v = dba->InsertVertex();
v.add_label(label_);
constraint_.UpdateOnAddLabel(label_, v, dba->transaction());
v.PropsSet(property3_, value3_);
constraint_.UpdateOnAddProperty(property3_, value3_, v, dba->transaction());
v.PropsSet(property2_, value2_);
constraint_.UpdateOnAddProperty(property2_, value2_, v, dba->transaction());
v.PropsSet(property1_, value1_);
EXPECT_THROW(constraint_.UpdateOnAddProperty(property1_, value1_, v,
dba->transaction()),
database::IndexConstraintViolationException);
dba->Commit();
}
}
TEST_F(UniqueLabelPropertiesTest, InsertInsertDiffValues) {
{
auto dba = db_.Access();
auto v = dba->InsertVertex();
v.add_label(label_);
constraint_.UpdateOnAddLabel(label_, v, dba->transaction());
v.PropsSet(property2_, value2_);
constraint_.UpdateOnAddProperty(property2_, value2_, v, dba->transaction());
v.PropsSet(property1_, value1_);
constraint_.UpdateOnAddProperty(property1_, value1_, v, dba->transaction());
v.PropsSet(property3_, value3_);
constraint_.UpdateOnAddProperty(property3_, value3_, v, dba->transaction());
dba->Commit();
}
{
auto dba = db_.Access();
auto v = dba->InsertVertex();
PropertyValue other3("Some other value 3");
v.PropsSet(property3_, other3);
constraint_.UpdateOnAddProperty(property3_, other3, v, dba->transaction());
v.add_label(label_);
constraint_.UpdateOnAddLabel(label_, v, dba->transaction());
PropertyValue other2("Some other value 2");
v.PropsSet(property2_, other2);
constraint_.UpdateOnAddProperty(property2_, other2, v, dba->transaction());
PropertyValue other1("Some other value 1");
v.PropsSet(property1_, other1);
constraint_.UpdateOnAddProperty(property1_, other1, v, dba->transaction());
dba->Commit();
}
}
TEST_F(UniqueLabelPropertiesTest, InsertAbortInsert) {
{
auto dba = db_.Access();
auto v = dba->InsertVertex();
v.PropsSet(property1_, value1_);
constraint_.UpdateOnAddProperty(property1_, value1_, v, dba->transaction());
v.add_label(label_);
constraint_.UpdateOnAddLabel(label_, v, dba->transaction());
v.PropsSet(property2_, value2_);
constraint_.UpdateOnAddProperty(property2_, value2_, v, dba->transaction());
v.PropsSet(property3_, value3_);
constraint_.UpdateOnAddProperty(property3_, value3_, v, dba->transaction());
dba->Abort();
}
{
auto dba = db_.Access();
auto v = dba->InsertVertex();
v.PropsSet(property2_, value2_);
constraint_.UpdateOnAddProperty(property2_, value2_, v, dba->transaction());
v.PropsSet(property1_, value1_);
constraint_.UpdateOnAddProperty(property1_, value1_, v, dba->transaction());
v.PropsSet(property3_, value3_);
constraint_.UpdateOnAddProperty(property3_, value3_, v, dba->transaction());
v.add_label(label_);
constraint_.UpdateOnAddLabel(label_, v, dba->transaction());
dba->Commit();
}
}
TEST_F(UniqueLabelPropertiesTest, InsertRemoveAbortInsert) {
gid::Gid gid = 0;
{
auto dba = db_.Access();
auto v = dba->InsertVertex();
v.PropsSet(property2_, value2_);
constraint_.UpdateOnAddProperty(property2_, value2_, v, dba->transaction());
v.PropsSet(property1_, value1_);
constraint_.UpdateOnAddProperty(property1_, value1_, v, dba->transaction());
v.PropsSet(property3_, value3_);
constraint_.UpdateOnAddProperty(property3_, value3_, v, dba->transaction());
v.add_label(label_);
constraint_.UpdateOnAddLabel(label_, v, dba->transaction());
dba->Commit();
}
{
auto dba = db_.Access();
auto v = dba->FindVertex(gid, false);
v.PropsErase(property2_);
constraint_.UpdateOnRemoveProperty(property2_, value2_, v,
dba->transaction());
dba->Abort();
}
{
auto dba = db_.Access();
auto v = dba->InsertVertex();
v.PropsSet(property1_, value1_);
constraint_.UpdateOnAddProperty(property1_, value1_, v, dba->transaction());
v.add_label(label_);
constraint_.UpdateOnAddLabel(label_, v, dba->transaction());
v.PropsSet(property2_, value2_);
constraint_.UpdateOnAddProperty(property2_, value2_, v, dba->transaction());
v.PropsSet(property3_, value3_);
EXPECT_THROW(constraint_.UpdateOnAddProperty(property3_, value3_, v,
dba->transaction()),
database::IndexConstraintViolationException);
}
}
TEST_F(UniqueLabelPropertiesTest, InsertInsertSameTransaction) {
{
auto dba = db_.Access();
auto v1 = dba->InsertVertex();
auto v2 = dba->InsertVertex();
v2.add_label(label_);
constraint_.UpdateOnAddLabel(label_, v2, dba->transaction());
v1.add_label(label_);
constraint_.UpdateOnAddLabel(label_, v1, dba->transaction());
v1.PropsSet(property1_, value1_);
constraint_.UpdateOnAddProperty(property1_, value1_, v1,
dba->transaction());
v1.PropsSet(property2_, value2_);
constraint_.UpdateOnAddProperty(property2_, value2_, v1,
dba->transaction());
v2.PropsSet(property2_, value2_);
constraint_.UpdateOnAddProperty(property2_, value2_, v2,
dba->transaction());
v2.PropsSet(property3_, value3_);
constraint_.UpdateOnAddProperty(property3_, value3_, v2,
dba->transaction());
v2.PropsSet(property1_, value1_);
constraint_.UpdateOnAddProperty(property1_, value1_, v2,
dba->transaction());
v1.PropsSet(property3_, value3_);
EXPECT_THROW(constraint_.UpdateOnAddProperty(property3_, value3_, v1,
dba->transaction()),
database::IndexConstraintViolationException);
}
}
TEST_F(UniqueLabelPropertiesTest, InsertInsertReversed) {
auto dba1 = db_.Access();
auto dba2 = db_.Access();
auto v1 = dba1->InsertVertex();
auto v2 = dba2->InsertVertex();
v2.add_label(label_);
constraint_.UpdateOnAddLabel(label_, v2, dba2->transaction());
v1.add_label(label_);
constraint_.UpdateOnAddLabel(label_, v1, dba1->transaction());
v1.PropsSet(property1_, value1_);
constraint_.UpdateOnAddProperty(property1_, value1_, v1,
dba1->transaction());
v1.PropsSet(property2_, value2_);
constraint_.UpdateOnAddProperty(property2_, value2_, v1,
dba1->transaction());
v2.PropsSet(property2_, value2_);
constraint_.UpdateOnAddProperty(property2_, value2_, v2,
dba2->transaction());
v2.PropsSet(property3_, value3_);
constraint_.UpdateOnAddProperty(property3_, value3_, v2,
dba2->transaction());
v2.PropsSet(property1_, value1_);
constraint_.UpdateOnAddProperty(property1_, value1_, v2,
dba2->transaction());
v1.PropsSet(property3_, value3_);
EXPECT_THROW(constraint_.UpdateOnAddProperty(property3_, value3_, v1,
dba1->transaction()),
mvcc::SerializationError);
}
TEST_F(UniqueLabelPropertiesTest, InsertRemoveInsert) {
gid::Gid gid = 0;
{
auto dba = db_.Access();
auto v = dba->InsertVertex();
v.PropsSet(property2_, value2_);
constraint_.UpdateOnAddProperty(property2_, value2_, v, dba->transaction());
v.PropsSet(property1_, value1_);
constraint_.UpdateOnAddProperty(property1_, value1_, v, dba->transaction());
v.PropsSet(property3_, value3_);
constraint_.UpdateOnAddProperty(property3_, value3_, v, dba->transaction());
v.add_label(label_);
constraint_.UpdateOnAddLabel(label_, v, dba->transaction());
dba->Commit();
}
{
auto dba = db_.Access();
auto v = dba->FindVertex(gid, false);
v.PropsErase(property2_);
constraint_.UpdateOnRemoveProperty(property2_, value2_, v,
dba->transaction());
dba->Commit();
}
{
auto dba = db_.Access();
auto v = dba->InsertVertex();
v.PropsSet(property1_, value1_);
constraint_.UpdateOnAddProperty(property1_, value1_, v, dba->transaction());
v.add_label(label_);
constraint_.UpdateOnAddLabel(label_, v, dba->transaction());
v.PropsSet(property2_, value2_);
constraint_.UpdateOnAddProperty(property2_, value2_, v, dba->transaction());
v.PropsSet(property3_, value3_);
constraint_.UpdateOnAddProperty(property3_, value3_, v, dba->transaction());
}
}
TEST_F(UniqueLabelPropertiesTest, InsertRemoveInsertSameTransaction) {
auto dba = db_.Access();
auto v = dba->InsertVertex();
v.PropsSet(property2_, value2_);
constraint_.UpdateOnAddProperty(property2_, value2_, v, dba->transaction());
v.PropsSet(property1_, value1_);
constraint_.UpdateOnAddProperty(property1_, value1_, v, dba->transaction());
v.PropsSet(property3_, value3_);
constraint_.UpdateOnAddProperty(property3_, value3_, v, dba->transaction());
v.add_label(label_);
constraint_.UpdateOnAddLabel(label_, v, dba->transaction());
v.PropsErase(property2_);
constraint_.UpdateOnRemoveProperty(property2_, value2_, v,
dba->transaction());
v.PropsSet(property2_, value2_);
constraint_.UpdateOnAddProperty(property2_, value2_, v, dba->transaction());
}
TEST_F(UniqueLabelPropertiesTest, InsertDropInsert) {
{
auto dba = db_.Access();
auto v = dba->InsertVertex();
v.PropsSet(property1_, value1_);
constraint_.UpdateOnAddProperty(property1_, value1_, v, dba->transaction());
v.add_label(label_);
constraint_.UpdateOnAddLabel(label_, v, dba->transaction());
v.PropsSet(property2_, value2_);
constraint_.UpdateOnAddProperty(property2_, value2_, v, dba->transaction());
v.PropsSet(property3_, value3_);
constraint_.UpdateOnAddProperty(property3_, value3_, v, dba->transaction());
}
{
auto dba = db_.AccessBlocking();
constraint_.RemoveConstraint(label_, {property2_, property3_, property1_});
dba->Commit();
}
{
auto dba = db_.Access();
auto v = dba->InsertVertex();
v.PropsSet(property2_, value2_);
constraint_.UpdateOnAddProperty(property2_, value2_, v, dba->transaction());
v.PropsSet(property1_, value1_);
constraint_.UpdateOnAddProperty(property1_, value1_, v, dba->transaction());
v.add_label(label_);
constraint_.UpdateOnAddLabel(label_, v, dba->transaction());
v.PropsSet(property3_, value3_);
constraint_.UpdateOnAddProperty(property3_, value3_, v, dba->transaction());
dba->Commit();
}
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
google::InitGoogleLogging(argv[0]);
return RUN_ALL_TESTS();
}