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:
parent
e1ad5cd803
commit
47a1e302a0
@ -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
|
||||
|
28
src/storage/single_node/constraints/common.hpp
Normal file
28
src/storage/single_node/constraints/common.hpp
Normal 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
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
@ -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
|
@ -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
|
||||
|
@ -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)
|
||||
|
376
tests/unit/unique_label_properties_constraint.cpp
Normal file
376
tests/unit/unique_label_properties_constraint.cpp
Normal 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();
|
||||
}
|
Loading…
Reference in New Issue
Block a user