78a88737f8
Co-authored-by: antoniofilipovic <filipovicantonio1998@gmail.com>
1266 lines
47 KiB
C++
1266 lines
47 KiB
C++
// Copyright 2024 Memgraph Ltd.
|
|
//
|
|
// Use of this software is governed by the Business Source License
|
|
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
|
// License, and you may not use this file except in compliance with the Business Source License.
|
|
//
|
|
// As of the Change Date specified in that file, in accordance with
|
|
// the Business Source License, use of this software will be governed
|
|
// by the Apache License, Version 2.0, included in the file
|
|
// licenses/APL.txt.
|
|
|
|
#include <gmock/gmock.h>
|
|
#include <gtest/gtest.h>
|
|
#include <filesystem>
|
|
#include <type_traits>
|
|
#include <variant>
|
|
|
|
#include "dbms/database.hpp"
|
|
#include "storage/v2/constraints/constraints.hpp"
|
|
#include "storage/v2/disk/storage.hpp"
|
|
#include "storage/v2/disk/unique_constraints.hpp"
|
|
#include "storage/v2/inmemory/storage.hpp"
|
|
|
|
#include "disk_test_utils.hpp"
|
|
|
|
using memgraph::replication_coordination_glue::ReplicationRole;
|
|
|
|
// NOLINTNEXTLINE(google-build-using-namespace)
|
|
using namespace memgraph::storage;
|
|
|
|
using testing::Types;
|
|
using testing::UnorderedElementsAre;
|
|
|
|
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
|
|
#define ASSERT_NO_ERROR(result) ASSERT_FALSE((result).HasError())
|
|
|
|
template <typename StorageType>
|
|
class ConstraintsTest : public testing::Test {
|
|
public:
|
|
const std::string testSuite = "storage_v2_constraints";
|
|
|
|
ConstraintsTest() {
|
|
/// TODO: andi How to make this better? Because currentlly for every test changed you need to create a configuration
|
|
config_ = disk_test_utils::GenerateOnDiskConfig(testSuite);
|
|
config_.force_on_disk = std::is_same_v<StorageType, memgraph::storage::DiskStorage>;
|
|
repl_state_.emplace(memgraph::storage::ReplicationStateRootPath(config_));
|
|
db_gk_.emplace(config_, *repl_state_);
|
|
auto db_acc_opt = db_gk_->access();
|
|
MG_ASSERT(db_acc_opt, "Failed to access db");
|
|
db_acc_ = *db_acc_opt;
|
|
storage = db_acc_->get()->storage();
|
|
prop1 = storage->NameToProperty("prop1");
|
|
prop2 = storage->NameToProperty("prop2");
|
|
label1 = storage->NameToLabel("label1");
|
|
label2 = storage->NameToLabel("label2");
|
|
}
|
|
|
|
void TearDown() override {
|
|
storage = nullptr;
|
|
db_acc_.reset();
|
|
db_gk_.reset();
|
|
repl_state_.reset();
|
|
|
|
if (std::is_same<StorageType, memgraph::storage::DiskStorage>::value) {
|
|
disk_test_utils::RemoveRocksDbDirs(testSuite);
|
|
}
|
|
}
|
|
|
|
Storage *storage;
|
|
memgraph::storage::Config config_;
|
|
std::optional<memgraph::replication::ReplicationState> repl_state_;
|
|
std::optional<memgraph::dbms::DatabaseAccess> db_acc_;
|
|
std::optional<memgraph::utils::Gatekeeper<memgraph::dbms::Database>> db_gk_;
|
|
PropertyId prop1;
|
|
PropertyId prop2;
|
|
LabelId label1;
|
|
LabelId label2;
|
|
};
|
|
|
|
using StorageTypes = ::testing::Types<memgraph::storage::InMemoryStorage, memgraph::storage::DiskStorage>;
|
|
TYPED_TEST_CASE(ConstraintsTest, StorageTypes);
|
|
|
|
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
|
TYPED_TEST(ConstraintsTest, ExistenceConstraintsCreateAndDrop) {
|
|
{
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
EXPECT_EQ(acc->ListAllConstraints().existence.size(), 0);
|
|
ASSERT_NO_ERROR(acc->Commit());
|
|
}
|
|
{
|
|
auto unique_acc = this->db_acc_->get()->UniqueAccess();
|
|
auto res = unique_acc->CreateExistenceConstraint(this->label1, this->prop1);
|
|
EXPECT_FALSE(res.HasError());
|
|
ASSERT_FALSE(unique_acc->Commit().HasError());
|
|
}
|
|
{
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
EXPECT_THAT(acc->ListAllConstraints().existence, UnorderedElementsAre(std::make_pair(this->label1, this->prop1)));
|
|
ASSERT_NO_ERROR(acc->Commit());
|
|
}
|
|
{
|
|
auto unique_acc = this->db_acc_->get()->UniqueAccess();
|
|
auto res = unique_acc->CreateExistenceConstraint(this->label1, this->prop1);
|
|
EXPECT_TRUE(res.HasError());
|
|
ASSERT_FALSE(unique_acc->Commit().HasError());
|
|
}
|
|
{
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
EXPECT_THAT(acc->ListAllConstraints().existence, UnorderedElementsAre(std::make_pair(this->label1, this->prop1)));
|
|
ASSERT_NO_ERROR(acc->Commit());
|
|
}
|
|
{
|
|
auto unique_acc = this->db_acc_->get()->UniqueAccess();
|
|
auto res = unique_acc->CreateExistenceConstraint(this->label2, this->prop1);
|
|
EXPECT_FALSE(res.HasError());
|
|
ASSERT_FALSE(unique_acc->Commit().HasError());
|
|
}
|
|
{
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
EXPECT_THAT(acc->ListAllConstraints().existence, UnorderedElementsAre(std::make_pair(this->label1, this->prop1),
|
|
std::make_pair(this->label2, this->prop1)));
|
|
ASSERT_NO_ERROR(acc->Commit());
|
|
}
|
|
{
|
|
auto unique_acc = this->db_acc_->get()->UniqueAccess();
|
|
EXPECT_FALSE(unique_acc->DropExistenceConstraint(this->label1, this->prop1).HasError());
|
|
ASSERT_FALSE(unique_acc->Commit().HasError());
|
|
}
|
|
{
|
|
auto unique_acc = this->db_acc_->get()->UniqueAccess();
|
|
EXPECT_TRUE(unique_acc->DropExistenceConstraint(this->label1, this->prop1).HasError());
|
|
ASSERT_FALSE(unique_acc->Commit().HasError());
|
|
}
|
|
{
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
EXPECT_THAT(acc->ListAllConstraints().existence, UnorderedElementsAre(std::make_pair(this->label2, this->prop1)));
|
|
ASSERT_NO_ERROR(acc->Commit());
|
|
}
|
|
{
|
|
auto unique_acc = this->db_acc_->get()->UniqueAccess();
|
|
EXPECT_FALSE(unique_acc->DropExistenceConstraint(this->label2, this->prop1).HasError());
|
|
ASSERT_FALSE(unique_acc->Commit().HasError());
|
|
}
|
|
{
|
|
auto unique_acc = this->db_acc_->get()->UniqueAccess();
|
|
EXPECT_TRUE(unique_acc->DropExistenceConstraint(this->label2, this->prop2).HasError());
|
|
ASSERT_FALSE(unique_acc->Commit().HasError());
|
|
}
|
|
{
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
EXPECT_EQ(acc->ListAllConstraints().existence.size(), 0);
|
|
ASSERT_NO_ERROR(acc->Commit());
|
|
}
|
|
{
|
|
auto unique_acc = this->db_acc_->get()->UniqueAccess();
|
|
auto res = unique_acc->CreateExistenceConstraint(this->label2, this->prop1);
|
|
EXPECT_FALSE(res.HasError());
|
|
ASSERT_FALSE(unique_acc->Commit().HasError());
|
|
}
|
|
{
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
EXPECT_THAT(acc->ListAllConstraints().existence, UnorderedElementsAre(std::make_pair(this->label2, this->prop1)));
|
|
ASSERT_NO_ERROR(acc->Commit());
|
|
}
|
|
}
|
|
|
|
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
|
TYPED_TEST(ConstraintsTest, ExistenceConstraintsCreateFailure1) {
|
|
{
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
auto vertex = acc->CreateVertex();
|
|
ASSERT_NO_ERROR(vertex.AddLabel(this->label1));
|
|
ASSERT_NO_ERROR(acc->Commit());
|
|
}
|
|
{
|
|
auto unique_acc = this->db_acc_->get()->UniqueAccess();
|
|
auto res = unique_acc->CreateExistenceConstraint(this->label1, this->prop1);
|
|
ASSERT_TRUE(res.HasError());
|
|
EXPECT_EQ(
|
|
std::get<ConstraintViolation>(res.GetError()),
|
|
(ConstraintViolation{ConstraintViolation::Type::EXISTENCE, this->label1, std::set<PropertyId>{this->prop1}}));
|
|
ASSERT_FALSE(unique_acc->Commit().HasError()); // TODO: Check if we are committing here?
|
|
}
|
|
{
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
for (auto vertex : acc->Vertices(View::OLD)) {
|
|
ASSERT_NO_ERROR(acc->DeleteVertex(&vertex));
|
|
}
|
|
ASSERT_NO_ERROR(acc->Commit());
|
|
}
|
|
{
|
|
auto unique_acc = this->db_acc_->get()->UniqueAccess();
|
|
auto res = unique_acc->CreateExistenceConstraint(this->label1, this->prop1);
|
|
EXPECT_FALSE(res.HasError());
|
|
ASSERT_NO_ERROR(unique_acc->Commit());
|
|
}
|
|
}
|
|
|
|
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
|
TYPED_TEST(ConstraintsTest, ExistenceConstraintsCreateFailure2) {
|
|
{
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
auto vertex = acc->CreateVertex();
|
|
ASSERT_NO_ERROR(vertex.AddLabel(this->label1));
|
|
ASSERT_NO_ERROR(acc->Commit());
|
|
}
|
|
{
|
|
auto unique_acc = this->db_acc_->get()->UniqueAccess();
|
|
auto res = unique_acc->CreateExistenceConstraint(this->label1, this->prop1);
|
|
ASSERT_TRUE(res.HasError());
|
|
EXPECT_EQ(
|
|
std::get<ConstraintViolation>(res.GetError()),
|
|
(ConstraintViolation{ConstraintViolation::Type::EXISTENCE, this->label1, std::set<PropertyId>{this->prop1}}));
|
|
ASSERT_FALSE(unique_acc->Commit().HasError()); // TODO: Check if we are committing here?
|
|
}
|
|
{
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
for (auto vertex : acc->Vertices(View::OLD)) {
|
|
ASSERT_NO_ERROR(vertex.SetProperty(this->prop1, PropertyValue(1)));
|
|
}
|
|
ASSERT_NO_ERROR(acc->Commit());
|
|
}
|
|
{
|
|
auto unique_acc = this->db_acc_->get()->UniqueAccess();
|
|
auto res = unique_acc->CreateExistenceConstraint(this->label1, this->prop1);
|
|
EXPECT_FALSE(res.HasError());
|
|
ASSERT_NO_ERROR(unique_acc->Commit());
|
|
}
|
|
}
|
|
|
|
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
|
TYPED_TEST(ConstraintsTest, ExistenceConstraintsViolationOnCommit) {
|
|
{
|
|
auto unique_acc = this->db_acc_->get()->UniqueAccess();
|
|
auto res = unique_acc->CreateExistenceConstraint(this->label1, this->prop1);
|
|
EXPECT_FALSE(res.HasError());
|
|
ASSERT_NO_ERROR(unique_acc->Commit());
|
|
}
|
|
|
|
{
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
auto vertex = acc->CreateVertex();
|
|
ASSERT_NO_ERROR(vertex.AddLabel(this->label1));
|
|
|
|
auto res = acc->Commit();
|
|
ASSERT_TRUE(res.HasError());
|
|
EXPECT_EQ(
|
|
std::get<ConstraintViolation>(res.GetError()),
|
|
(ConstraintViolation{ConstraintViolation::Type::EXISTENCE, this->label1, std::set<PropertyId>{this->prop1}}));
|
|
}
|
|
|
|
{
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
auto vertex = acc->CreateVertex();
|
|
ASSERT_NO_ERROR(vertex.AddLabel(this->label1));
|
|
ASSERT_NO_ERROR(vertex.SetProperty(this->prop1, PropertyValue(1)));
|
|
ASSERT_NO_ERROR(acc->Commit());
|
|
}
|
|
|
|
{
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
for (auto vertex : acc->Vertices(View::OLD)) {
|
|
ASSERT_NO_ERROR(vertex.SetProperty(this->prop1, PropertyValue()));
|
|
}
|
|
|
|
auto res = acc->Commit();
|
|
ASSERT_TRUE(res.HasError());
|
|
EXPECT_EQ(
|
|
std::get<ConstraintViolation>(res.GetError()),
|
|
(ConstraintViolation{ConstraintViolation::Type::EXISTENCE, this->label1, std::set<PropertyId>{this->prop1}}));
|
|
}
|
|
|
|
{
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
for (auto vertex : acc->Vertices(View::OLD)) {
|
|
ASSERT_NO_ERROR(vertex.SetProperty(this->prop1, PropertyValue()));
|
|
}
|
|
for (auto vertex : acc->Vertices(View::OLD)) {
|
|
ASSERT_NO_ERROR(acc->DeleteVertex(&vertex));
|
|
}
|
|
|
|
ASSERT_NO_ERROR(acc->Commit());
|
|
}
|
|
{
|
|
auto unique_acc = this->db_acc_->get()->UniqueAccess();
|
|
ASSERT_FALSE(unique_acc->DropExistenceConstraint(this->label1, this->prop1).HasError());
|
|
ASSERT_NO_ERROR(unique_acc->Commit());
|
|
}
|
|
{
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
auto vertex = acc->CreateVertex();
|
|
ASSERT_NO_ERROR(vertex.AddLabel(this->label1));
|
|
ASSERT_NO_ERROR(acc->Commit());
|
|
}
|
|
}
|
|
|
|
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
|
TYPED_TEST(ConstraintsTest, UniqueConstraintsCreateAndDropAndList) {
|
|
{
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
EXPECT_EQ(acc->ListAllConstraints().unique.size(), 0);
|
|
ASSERT_NO_ERROR(acc->Commit());
|
|
}
|
|
{
|
|
auto unique_acc = this->db_acc_->get()->UniqueAccess();
|
|
auto res = unique_acc->CreateUniqueConstraint(this->label1, {this->prop1});
|
|
EXPECT_TRUE(res.HasValue());
|
|
EXPECT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
|
|
ASSERT_NO_ERROR(unique_acc->Commit());
|
|
}
|
|
{
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
EXPECT_THAT(acc->ListAllConstraints().unique,
|
|
UnorderedElementsAre(std::make_pair(this->label1, std::set<PropertyId>{this->prop1})));
|
|
ASSERT_NO_ERROR(acc->Commit());
|
|
}
|
|
{
|
|
auto unique_acc = this->db_acc_->get()->UniqueAccess();
|
|
auto res = unique_acc->CreateUniqueConstraint(this->label1, {this->prop1});
|
|
EXPECT_TRUE(res.HasValue());
|
|
EXPECT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::ALREADY_EXISTS);
|
|
ASSERT_NO_ERROR(unique_acc->Commit());
|
|
}
|
|
{
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
EXPECT_THAT(acc->ListAllConstraints().unique,
|
|
UnorderedElementsAre(std::make_pair(this->label1, std::set<PropertyId>{this->prop1})));
|
|
ASSERT_NO_ERROR(acc->Commit());
|
|
}
|
|
{
|
|
auto unique_acc = this->db_acc_->get()->UniqueAccess();
|
|
auto res = unique_acc->CreateUniqueConstraint(this->label2, {this->prop1});
|
|
EXPECT_TRUE(res.HasValue() && res.GetValue() == UniqueConstraints::CreationStatus::SUCCESS);
|
|
ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
|
|
ASSERT_NO_ERROR(unique_acc->Commit());
|
|
}
|
|
{
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
EXPECT_THAT(acc->ListAllConstraints().unique,
|
|
UnorderedElementsAre(std::make_pair(this->label1, std::set<PropertyId>{this->prop1}),
|
|
std::make_pair(this->label2, std::set<PropertyId>{this->prop1})));
|
|
ASSERT_NO_ERROR(acc->Commit());
|
|
}
|
|
{
|
|
auto unique_acc = this->db_acc_->get()->UniqueAccess();
|
|
EXPECT_EQ(unique_acc->DropUniqueConstraint(this->label1, {this->prop1}),
|
|
UniqueConstraints::DeletionStatus::SUCCESS);
|
|
ASSERT_NO_ERROR(unique_acc->Commit());
|
|
}
|
|
{
|
|
auto unique_acc = this->db_acc_->get()->UniqueAccess();
|
|
EXPECT_EQ(unique_acc->DropUniqueConstraint(this->label1, {this->prop1}),
|
|
UniqueConstraints::DeletionStatus::NOT_FOUND);
|
|
ASSERT_NO_ERROR(unique_acc->Commit());
|
|
}
|
|
{
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
EXPECT_THAT(acc->ListAllConstraints().unique,
|
|
UnorderedElementsAre(std::make_pair(this->label2, std::set<PropertyId>{this->prop1})));
|
|
ASSERT_NO_ERROR(acc->Commit());
|
|
}
|
|
{
|
|
auto unique_acc = this->db_acc_->get()->UniqueAccess();
|
|
EXPECT_EQ(unique_acc->DropUniqueConstraint(this->label2, {this->prop1}),
|
|
UniqueConstraints::DeletionStatus::SUCCESS);
|
|
ASSERT_NO_ERROR(unique_acc->Commit());
|
|
}
|
|
{
|
|
auto unique_acc = this->db_acc_->get()->UniqueAccess();
|
|
EXPECT_EQ(unique_acc->DropUniqueConstraint(this->label2, {this->prop2}),
|
|
UniqueConstraints::DeletionStatus::NOT_FOUND);
|
|
ASSERT_NO_ERROR(unique_acc->Commit());
|
|
}
|
|
{
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
EXPECT_EQ(acc->ListAllConstraints().unique.size(), 0);
|
|
ASSERT_NO_ERROR(acc->Commit());
|
|
}
|
|
{
|
|
auto unique_acc = this->db_acc_->get()->UniqueAccess();
|
|
auto res = unique_acc->CreateUniqueConstraint(this->label2, {this->prop1});
|
|
EXPECT_TRUE(res.HasValue());
|
|
EXPECT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
|
|
}
|
|
{
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
EXPECT_THAT(acc->ListAllConstraints().unique,
|
|
UnorderedElementsAre(std::make_pair(this->label2, std::set<PropertyId>{this->prop1})));
|
|
ASSERT_NO_ERROR(acc->Commit());
|
|
}
|
|
}
|
|
|
|
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
|
TYPED_TEST(ConstraintsTest, UniqueConstraintsCreateFailure1) {
|
|
{
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
for (int i = 0; i < 2; ++i) {
|
|
auto vertex1 = acc->CreateVertex();
|
|
ASSERT_NO_ERROR(vertex1.AddLabel(this->label1));
|
|
ASSERT_NO_ERROR(vertex1.SetProperty(this->prop1, PropertyValue(1)));
|
|
}
|
|
ASSERT_NO_ERROR(acc->Commit());
|
|
}
|
|
|
|
{
|
|
auto unique_acc = this->db_acc_->get()->UniqueAccess();
|
|
auto res = unique_acc->CreateUniqueConstraint(this->label1, {this->prop1});
|
|
ASSERT_TRUE(res.HasError());
|
|
EXPECT_EQ(
|
|
std::get<ConstraintViolation>(res.GetError()),
|
|
(ConstraintViolation{ConstraintViolation::Type::UNIQUE, this->label1, std::set<PropertyId>{this->prop1}}));
|
|
ASSERT_FALSE(unique_acc->Commit().HasError()); // TODO: Check if we are committing here?
|
|
}
|
|
|
|
{
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
for (auto vertex : acc->Vertices(View::OLD)) {
|
|
ASSERT_NO_ERROR(acc->DeleteVertex(&vertex));
|
|
}
|
|
ASSERT_NO_ERROR(acc->Commit());
|
|
}
|
|
|
|
{
|
|
auto unique_acc = this->db_acc_->get()->UniqueAccess();
|
|
auto res = unique_acc->CreateUniqueConstraint(this->label1, {this->prop1});
|
|
ASSERT_TRUE(res.HasValue());
|
|
ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
|
|
ASSERT_NO_ERROR(unique_acc->Commit());
|
|
}
|
|
}
|
|
|
|
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
|
TYPED_TEST(ConstraintsTest, UniqueConstraintsCreateFailure2) {
|
|
{
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
for (int i = 0; i < 2; ++i) {
|
|
auto vertex = acc->CreateVertex();
|
|
ASSERT_NO_ERROR(vertex.AddLabel(this->label1));
|
|
ASSERT_NO_ERROR(vertex.SetProperty(this->prop1, PropertyValue(1)));
|
|
}
|
|
ASSERT_NO_ERROR(acc->Commit());
|
|
}
|
|
|
|
{
|
|
auto unique_acc = this->db_acc_->get()->UniqueAccess();
|
|
auto res = unique_acc->CreateUniqueConstraint(this->label1, {this->prop1});
|
|
ASSERT_TRUE(res.HasError());
|
|
EXPECT_EQ(
|
|
std::get<ConstraintViolation>(res.GetError()),
|
|
(ConstraintViolation{ConstraintViolation::Type::UNIQUE, this->label1, std::set<PropertyId>{this->prop1}}));
|
|
ASSERT_FALSE(unique_acc->Commit().HasError()); // TODO: Check if we are committing here?
|
|
}
|
|
|
|
{
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
int value = 0;
|
|
for (auto vertex : acc->Vertices(View::OLD)) {
|
|
ASSERT_NO_ERROR(vertex.SetProperty(this->prop1, PropertyValue(value)));
|
|
++value;
|
|
}
|
|
ASSERT_NO_ERROR(acc->Commit());
|
|
}
|
|
|
|
{
|
|
auto unique_acc = this->db_acc_->get()->UniqueAccess();
|
|
auto res = unique_acc->CreateUniqueConstraint(this->label1, {this->prop1});
|
|
ASSERT_TRUE(res.HasValue());
|
|
ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
|
|
ASSERT_NO_ERROR(unique_acc->Commit());
|
|
}
|
|
}
|
|
|
|
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
|
TYPED_TEST(ConstraintsTest, UniqueConstraintsNoViolation1) {
|
|
Gid gid1;
|
|
Gid gid2;
|
|
{
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
auto vertex1 = acc->CreateVertex();
|
|
auto vertex2 = acc->CreateVertex();
|
|
gid1 = vertex1.Gid();
|
|
gid2 = vertex2.Gid();
|
|
|
|
ASSERT_NO_ERROR(vertex1.AddLabel(this->label1));
|
|
ASSERT_NO_ERROR(vertex1.SetProperty(this->prop1, PropertyValue(1)));
|
|
ASSERT_NO_ERROR(acc->Commit());
|
|
}
|
|
|
|
{
|
|
auto unique_acc = this->db_acc_->get()->UniqueAccess();
|
|
auto res = unique_acc->CreateUniqueConstraint(this->label1, {this->prop1, this->prop2});
|
|
ASSERT_TRUE(res.HasValue());
|
|
ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
|
|
ASSERT_NO_ERROR(unique_acc->Commit());
|
|
}
|
|
|
|
{
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
auto vertex1 = acc->FindVertex(gid1, View::OLD);
|
|
auto vertex2 = acc->FindVertex(gid2, View::OLD);
|
|
|
|
ASSERT_NO_ERROR(vertex1->SetProperty(this->prop2, PropertyValue(2)));
|
|
ASSERT_NO_ERROR(vertex2->AddLabel(this->label1));
|
|
ASSERT_NO_ERROR(vertex2->SetProperty(this->prop1, PropertyValue(1)));
|
|
ASSERT_NO_ERROR(vertex2->SetProperty(this->prop2, PropertyValue(3)));
|
|
ASSERT_NO_ERROR(acc->Commit());
|
|
}
|
|
|
|
{
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
auto vertex1 = acc->FindVertex(gid1, View::OLD);
|
|
auto vertex2 = acc->FindVertex(gid2, View::OLD);
|
|
ASSERT_NO_ERROR(vertex1->SetProperty(this->prop1, PropertyValue(2)));
|
|
ASSERT_NO_ERROR(vertex2->SetProperty(this->prop1, PropertyValue(1)));
|
|
ASSERT_NO_ERROR(acc->Commit());
|
|
}
|
|
}
|
|
|
|
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
|
TYPED_TEST(ConstraintsTest, UniqueConstraintsNoViolation2) {
|
|
{
|
|
auto unique_acc = this->db_acc_->get()->UniqueAccess();
|
|
auto res = unique_acc->CreateUniqueConstraint(this->label1, {this->prop1});
|
|
ASSERT_TRUE(res.HasValue());
|
|
ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
|
|
ASSERT_NO_ERROR(unique_acc->Commit());
|
|
}
|
|
|
|
{
|
|
// tx1: B---SP(v1, 1)---SP(v1, 2)---OK--
|
|
// tx2: -B---SP(v2, 2)---SP(v2, 1)---OK-
|
|
|
|
auto acc1 = this->storage->Access(ReplicationRole::MAIN);
|
|
auto acc2 = this->storage->Access(ReplicationRole::MAIN);
|
|
auto vertex1 = acc1->CreateVertex();
|
|
auto vertex2 = acc2->CreateVertex();
|
|
|
|
ASSERT_NO_ERROR(vertex1.AddLabel(this->label1));
|
|
ASSERT_NO_ERROR(vertex1.SetProperty(this->prop1, PropertyValue(1)));
|
|
ASSERT_NO_ERROR(vertex2.AddLabel(this->label1));
|
|
ASSERT_NO_ERROR(vertex2.SetProperty(this->prop1, PropertyValue(2)));
|
|
|
|
ASSERT_NO_ERROR(vertex1.SetProperty(this->prop1, PropertyValue(2)));
|
|
ASSERT_NO_ERROR(vertex2.SetProperty(this->prop1, PropertyValue(1)));
|
|
|
|
ASSERT_NO_ERROR(acc1->Commit());
|
|
ASSERT_NO_ERROR(acc2->Commit());
|
|
}
|
|
}
|
|
|
|
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
|
TYPED_TEST(ConstraintsTest, UniqueConstraintsNoViolation3) {
|
|
{
|
|
auto unique_acc = this->db_acc_->get()->UniqueAccess();
|
|
auto res = unique_acc->CreateUniqueConstraint(this->label1, {this->prop1});
|
|
ASSERT_TRUE(res.HasValue());
|
|
ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
|
|
ASSERT_NO_ERROR(unique_acc->Commit());
|
|
}
|
|
|
|
{
|
|
// tx1: B---SP(v1, 1)---OK----------------------
|
|
// tx2: --------------------B---SP(v1, 2)---OK--
|
|
// tx3: ---------------------B---SP(v2, 1)---OK-
|
|
|
|
auto acc1 = this->storage->Access(ReplicationRole::MAIN);
|
|
auto vertex1 = acc1->CreateVertex();
|
|
auto gid = vertex1.Gid();
|
|
|
|
ASSERT_NO_ERROR(vertex1.AddLabel(this->label1));
|
|
ASSERT_NO_ERROR(vertex1.SetProperty(this->prop1, PropertyValue(1)));
|
|
|
|
ASSERT_NO_ERROR(acc1->Commit());
|
|
|
|
auto acc2 = this->storage->Access(ReplicationRole::MAIN);
|
|
auto acc3 = this->storage->Access(ReplicationRole::MAIN);
|
|
auto vertex2 = acc2->FindVertex(gid, View::NEW); // vertex1 == vertex2
|
|
auto vertex3 = acc3->CreateVertex();
|
|
|
|
ASSERT_NO_ERROR(vertex2->SetProperty(this->prop1, PropertyValue(2)));
|
|
ASSERT_NO_ERROR(vertex3.AddLabel(this->label1));
|
|
ASSERT_NO_ERROR(vertex3.SetProperty(this->prop1, PropertyValue(1)));
|
|
|
|
ASSERT_NO_ERROR(acc2->Commit());
|
|
ASSERT_NO_ERROR(acc3->Commit());
|
|
}
|
|
}
|
|
|
|
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
|
TYPED_TEST(ConstraintsTest, UniqueConstraintsNoViolation4) {
|
|
{
|
|
auto unique_acc = this->db_acc_->get()->UniqueAccess();
|
|
auto res = unique_acc->CreateUniqueConstraint(this->label1, {this->prop1});
|
|
ASSERT_TRUE(res.HasValue());
|
|
ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
|
|
ASSERT_NO_ERROR(unique_acc->Commit());
|
|
}
|
|
|
|
{
|
|
// tx1: B---SP(v1, 1)---OK-----------------------
|
|
// tx2: --------------------B---SP(v2, 1)-----OK-
|
|
// tx3: ---------------------B---SP(v1, 2)---OK--
|
|
|
|
auto acc1 = this->storage->Access(ReplicationRole::MAIN);
|
|
auto vertex1 = acc1->CreateVertex();
|
|
auto gid = vertex1.Gid();
|
|
|
|
ASSERT_NO_ERROR(vertex1.SetProperty(this->prop1, PropertyValue(1)));
|
|
|
|
ASSERT_NO_ERROR(acc1->Commit());
|
|
|
|
auto acc2 = this->storage->Access(ReplicationRole::MAIN);
|
|
auto acc3 = this->storage->Access(ReplicationRole::MAIN);
|
|
auto vertex2 = acc2->CreateVertex();
|
|
auto vertex3 = acc3->FindVertex(gid, View::NEW);
|
|
|
|
ASSERT_NO_ERROR(vertex2.AddLabel(this->label1));
|
|
ASSERT_NO_ERROR(vertex2.SetProperty(this->prop1, PropertyValue(1)));
|
|
ASSERT_NO_ERROR(vertex3->SetProperty(this->prop1, PropertyValue(2)));
|
|
|
|
ASSERT_NO_ERROR(acc3->Commit());
|
|
ASSERT_NO_ERROR(acc2->Commit());
|
|
}
|
|
}
|
|
|
|
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
|
TYPED_TEST(ConstraintsTest, UniqueConstraintsViolationOnCommit1) {
|
|
{
|
|
auto unique_acc = this->db_acc_->get()->UniqueAccess();
|
|
auto res = unique_acc->CreateUniqueConstraint(this->label1, {this->prop1});
|
|
ASSERT_TRUE(res.HasValue());
|
|
ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
|
|
ASSERT_NO_ERROR(unique_acc->Commit());
|
|
}
|
|
|
|
{
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
auto vertex1 = acc->CreateVertex();
|
|
auto vertex2 = acc->CreateVertex();
|
|
ASSERT_NO_ERROR(vertex1.AddLabel(this->label1));
|
|
ASSERT_NO_ERROR(vertex1.SetProperty(this->prop1, PropertyValue(1)));
|
|
ASSERT_NO_ERROR(vertex2.AddLabel(this->label1));
|
|
ASSERT_NO_ERROR(vertex2.SetProperty(this->prop1, PropertyValue(1)));
|
|
auto res = acc->Commit();
|
|
ASSERT_TRUE(res.HasError());
|
|
EXPECT_EQ(
|
|
std::get<ConstraintViolation>(res.GetError()),
|
|
(ConstraintViolation{ConstraintViolation::Type::UNIQUE, this->label1, std::set<PropertyId>{this->prop1}}));
|
|
}
|
|
}
|
|
|
|
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
|
/// TODO: andi consistency problems
|
|
TYPED_TEST(ConstraintsTest, UniqueConstraintsViolationOnCommit2) {
|
|
{
|
|
auto unique_acc = this->db_acc_->get()->UniqueAccess();
|
|
auto res = unique_acc->CreateUniqueConstraint(this->label1, {this->prop1});
|
|
ASSERT_TRUE(res.HasValue());
|
|
ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
|
|
ASSERT_NO_ERROR(unique_acc->Commit());
|
|
}
|
|
|
|
{
|
|
// tx1: B---SP(v1, 1)---SP(v2, 2)---OK-----------------------
|
|
// tx2: -------------------------------B---SP(v1, 3)---OK----
|
|
// tx3: --------------------------------B---SP(v2, 3)---FAIL-
|
|
|
|
auto acc1 = this->storage->Access(ReplicationRole::MAIN);
|
|
auto vertex1 = acc1->CreateVertex();
|
|
auto vertex2 = acc1->CreateVertex();
|
|
auto gid1 = vertex1.Gid();
|
|
auto gid2 = vertex2.Gid();
|
|
|
|
ASSERT_NO_ERROR(vertex1.AddLabel(this->label1));
|
|
ASSERT_NO_ERROR(vertex1.SetProperty(this->prop1, PropertyValue(1)));
|
|
ASSERT_NO_ERROR(vertex2.AddLabel(this->label1));
|
|
ASSERT_NO_ERROR(vertex2.SetProperty(this->prop1, PropertyValue(2)));
|
|
|
|
ASSERT_NO_ERROR(acc1->Commit());
|
|
|
|
auto acc2 = this->storage->Access(ReplicationRole::MAIN);
|
|
auto acc3 = this->storage->Access(ReplicationRole::MAIN);
|
|
auto vertex3 = acc2->FindVertex(gid1, View::NEW); // vertex3 == vertex1
|
|
auto vertex4 = acc3->FindVertex(gid2, View::NEW); // vertex4 == vertex2
|
|
|
|
ASSERT_NO_ERROR(vertex3->SetProperty(this->prop1, PropertyValue(3)));
|
|
ASSERT_NO_ERROR(vertex4->SetProperty(this->prop1, PropertyValue(3)));
|
|
|
|
ASSERT_NO_ERROR(acc2->Commit());
|
|
auto res = acc3->Commit();
|
|
ASSERT_TRUE(res.HasError());
|
|
EXPECT_EQ(
|
|
std::get<ConstraintViolation>(res.GetError()),
|
|
(ConstraintViolation{ConstraintViolation::Type::UNIQUE, this->label1, std::set<PropertyId>{this->prop1}}));
|
|
}
|
|
}
|
|
|
|
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
|
/// TODO: andi consistency problems
|
|
TYPED_TEST(ConstraintsTest, UniqueConstraintsViolationOnCommit3) {
|
|
{
|
|
auto unique_acc = this->db_acc_->get()->UniqueAccess();
|
|
auto res = unique_acc->CreateUniqueConstraint(this->label1, {this->prop1});
|
|
ASSERT_TRUE(res.HasValue());
|
|
ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
|
|
ASSERT_NO_ERROR(unique_acc->Commit());
|
|
}
|
|
|
|
{
|
|
// tx1: B---SP(v1, 1)---SP(v2, 2)---OK-----------------------
|
|
// tx2: -------------------------------B---SP(v1, 2)---FAIL--
|
|
// tx3: --------------------------------B---SP(v2, 1)---FAIL-
|
|
|
|
auto acc1 = this->storage->Access(ReplicationRole::MAIN);
|
|
auto vertex1 = acc1->CreateVertex();
|
|
auto vertex2 = acc1->CreateVertex();
|
|
auto gid1 = vertex1.Gid();
|
|
auto gid2 = vertex2.Gid();
|
|
|
|
ASSERT_NO_ERROR(vertex1.AddLabel(this->label1));
|
|
ASSERT_NO_ERROR(vertex1.SetProperty(this->prop1, PropertyValue(1)));
|
|
ASSERT_NO_ERROR(vertex2.AddLabel(this->label1));
|
|
ASSERT_NO_ERROR(vertex2.SetProperty(this->prop1, PropertyValue(2)));
|
|
|
|
ASSERT_NO_ERROR(acc1->Commit());
|
|
|
|
auto acc2 = this->storage->Access(ReplicationRole::MAIN);
|
|
auto acc3 = this->storage->Access(ReplicationRole::MAIN);
|
|
auto vertex3 = acc2->FindVertex(gid1, View::OLD); // vertex3 == vertex1
|
|
auto vertex4 = acc3->FindVertex(gid2, View::OLD); // vertex4 == vertex2
|
|
|
|
// Setting `this->prop2` shouldn't affect the remaining code.
|
|
ASSERT_NO_ERROR(vertex3->SetProperty(this->prop2, PropertyValue(3)));
|
|
ASSERT_NO_ERROR(vertex4->SetProperty(this->prop2, PropertyValue(3)));
|
|
|
|
ASSERT_NO_ERROR(vertex3->SetProperty(this->prop1, PropertyValue(2)));
|
|
ASSERT_NO_ERROR(vertex4->SetProperty(this->prop1, PropertyValue(1)));
|
|
|
|
auto res = acc2->Commit();
|
|
ASSERT_TRUE(res.HasError());
|
|
EXPECT_EQ(
|
|
std::get<ConstraintViolation>(res.GetError()),
|
|
(ConstraintViolation{ConstraintViolation::Type::UNIQUE, this->label1, std::set<PropertyId>{this->prop1}}));
|
|
res = acc3->Commit();
|
|
ASSERT_TRUE(res.HasError());
|
|
EXPECT_EQ(
|
|
std::get<ConstraintViolation>(res.GetError()),
|
|
(ConstraintViolation{ConstraintViolation::Type::UNIQUE, this->label1, std::set<PropertyId>{this->prop1}}));
|
|
}
|
|
}
|
|
|
|
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
|
TYPED_TEST(ConstraintsTest, UniqueConstraintsLabelAlteration) {
|
|
{
|
|
auto unique_acc = this->db_acc_->get()->UniqueAccess();
|
|
auto res = unique_acc->CreateUniqueConstraint(this->label1, {this->prop1});
|
|
ASSERT_TRUE(res.HasValue());
|
|
ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
|
|
ASSERT_NO_ERROR(unique_acc->Commit());
|
|
}
|
|
|
|
Gid gid1;
|
|
Gid gid2;
|
|
{
|
|
// B---AL(v2)---SP(v1, 1)---SP(v2, 1)---OK
|
|
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
auto vertex1 = acc->CreateVertex();
|
|
auto vertex2 = acc->CreateVertex();
|
|
gid1 = vertex1.Gid();
|
|
gid2 = vertex2.Gid();
|
|
|
|
spdlog::debug("Vertex1 gid: {} Vertex2 gid: {}\n", gid1.ToString(), gid2.ToString());
|
|
|
|
ASSERT_NO_ERROR(vertex1.AddLabel(this->label2));
|
|
ASSERT_NO_ERROR(vertex1.SetProperty(this->prop1, PropertyValue(1)));
|
|
ASSERT_NO_ERROR(vertex2.AddLabel(this->label1));
|
|
ASSERT_NO_ERROR(vertex2.SetProperty(this->prop1, PropertyValue(1)));
|
|
|
|
ASSERT_NO_ERROR(acc->Commit());
|
|
}
|
|
|
|
{
|
|
// tx1: B---AL(v1)-----OK-
|
|
// tx2: -B---RL(v2)---OK--
|
|
|
|
auto acc1 = this->storage->Access(ReplicationRole::MAIN);
|
|
auto acc2 = this->storage->Access(ReplicationRole::MAIN);
|
|
auto vertex1 = acc1->FindVertex(gid1, View::OLD);
|
|
auto vertex2 = acc2->FindVertex(gid2, View::OLD);
|
|
|
|
ASSERT_NO_ERROR(vertex1->AddLabel(this->label1));
|
|
ASSERT_NO_ERROR(vertex2->RemoveLabel(this->label1));
|
|
|
|
// Reapplying labels shouldn't affect the remaining code.
|
|
ASSERT_NO_ERROR(vertex1->RemoveLabel(this->label1));
|
|
ASSERT_NO_ERROR(vertex2->AddLabel(this->label1));
|
|
ASSERT_NO_ERROR(vertex1->AddLabel(this->label1));
|
|
ASSERT_NO_ERROR(vertex2->RemoveLabel(this->label1));
|
|
ASSERT_NO_ERROR(vertex1->RemoveLabel(this->label2));
|
|
|
|
// Commit the second transaction.
|
|
ASSERT_NO_ERROR(acc2->Commit());
|
|
|
|
// Reapplying labels after first commit shouldn't affect the remaining code.
|
|
ASSERT_NO_ERROR(vertex1->RemoveLabel(this->label1));
|
|
ASSERT_NO_ERROR(vertex1->AddLabel(this->label1));
|
|
|
|
// Commit the first transaction.
|
|
ASSERT_NO_ERROR(acc1->Commit());
|
|
}
|
|
|
|
{
|
|
// B---AL(v2)---FAIL
|
|
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
auto vertex2 = acc->FindVertex(gid2, View::OLD);
|
|
ASSERT_NO_ERROR(vertex2->AddLabel(this->label1));
|
|
|
|
auto res = acc->Commit();
|
|
ASSERT_TRUE(res.HasError());
|
|
EXPECT_EQ(std::get<ConstraintViolation>(res.GetError()),
|
|
(ConstraintViolation{ConstraintViolation::Type::UNIQUE, this->label1, std::set{this->prop1}}));
|
|
}
|
|
|
|
{
|
|
// B---RL(v1)---OK
|
|
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
auto vertex1 = acc->FindVertex(gid1, View::OLD);
|
|
ASSERT_NO_ERROR(vertex1->RemoveLabel(this->label1));
|
|
ASSERT_NO_ERROR(acc->Commit());
|
|
}
|
|
|
|
{
|
|
// tx1: B---AL(v1)-----FAIL
|
|
// tx2: -B---AL(v2)---OK---
|
|
|
|
auto acc1 = this->storage->Access(ReplicationRole::MAIN);
|
|
auto acc2 = this->storage->Access(ReplicationRole::MAIN);
|
|
auto vertex1 = acc1->FindVertex(gid1, View::OLD);
|
|
auto vertex2 = acc2->FindVertex(gid2, View::OLD);
|
|
|
|
ASSERT_NO_ERROR(vertex1->AddLabel(this->label1));
|
|
ASSERT_NO_ERROR(vertex2->AddLabel(this->label1));
|
|
|
|
// Reapply everything.
|
|
ASSERT_NO_ERROR(vertex1->RemoveLabel(this->label1));
|
|
ASSERT_NO_ERROR(vertex2->RemoveLabel(this->label1));
|
|
ASSERT_NO_ERROR(vertex1->AddLabel(this->label1));
|
|
ASSERT_NO_ERROR(vertex2->AddLabel(this->label1));
|
|
|
|
ASSERT_NO_ERROR(acc2->Commit());
|
|
|
|
auto res = acc1->Commit();
|
|
ASSERT_TRUE(res.HasError());
|
|
EXPECT_EQ(std::get<ConstraintViolation>(res.GetError()),
|
|
(ConstraintViolation{ConstraintViolation::Type::UNIQUE, this->label1, std::set{this->prop1}}));
|
|
}
|
|
}
|
|
|
|
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
|
TYPED_TEST(ConstraintsTest, UniqueConstraintsPropertySetSize) {
|
|
{
|
|
// This should fail since unique constraint cannot be created for an empty
|
|
// property set.
|
|
auto unique_acc = this->db_acc_->get()->UniqueAccess();
|
|
auto res = unique_acc->CreateUniqueConstraint(this->label1, {});
|
|
ASSERT_TRUE(res.HasValue());
|
|
ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::EMPTY_PROPERTIES);
|
|
ASSERT_NO_ERROR(unique_acc->Commit());
|
|
}
|
|
|
|
{ // Removing a constraint with empty property set should also fail.
|
|
auto unique_acc = this->db_acc_->get()->UniqueAccess();
|
|
ASSERT_EQ(unique_acc->DropUniqueConstraint(this->label1, {}), UniqueConstraints::DeletionStatus::EMPTY_PROPERTIES);
|
|
ASSERT_NO_ERROR(unique_acc->Commit());
|
|
}
|
|
|
|
// Create a set of 33 properties.
|
|
std::set<PropertyId> properties;
|
|
for (int i = 1; i <= 33; ++i) {
|
|
properties.insert(this->storage->NameToProperty("prop" + std::to_string(i)));
|
|
}
|
|
|
|
{
|
|
// This should fail since list of properties exceeds the maximum number of
|
|
// properties, which is 32.
|
|
auto unique_acc = this->db_acc_->get()->UniqueAccess();
|
|
auto res = unique_acc->CreateUniqueConstraint(this->label1, properties);
|
|
ASSERT_TRUE(res.HasValue());
|
|
ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::PROPERTIES_SIZE_LIMIT_EXCEEDED);
|
|
ASSERT_NO_ERROR(unique_acc->Commit());
|
|
}
|
|
|
|
{ // An attempt to delete constraint with too large property set should fail.
|
|
auto unique_acc = this->db_acc_->get()->UniqueAccess();
|
|
ASSERT_EQ(unique_acc->DropUniqueConstraint(this->label1, properties),
|
|
UniqueConstraints::DeletionStatus::PROPERTIES_SIZE_LIMIT_EXCEEDED);
|
|
ASSERT_NO_ERROR(unique_acc->Commit());
|
|
}
|
|
|
|
// Remove one property from the set.
|
|
properties.erase(properties.begin());
|
|
|
|
{
|
|
// Creating a constraint for 32 properties should succeed.
|
|
auto unique_acc = this->db_acc_->get()->UniqueAccess();
|
|
auto res = unique_acc->CreateUniqueConstraint(this->label1, properties);
|
|
ASSERT_TRUE(res.HasValue());
|
|
ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
|
|
ASSERT_NO_ERROR(unique_acc->Commit());
|
|
}
|
|
|
|
{
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
EXPECT_THAT(acc->ListAllConstraints().unique, UnorderedElementsAre(std::make_pair(this->label1, properties)));
|
|
ASSERT_NO_ERROR(acc->Commit());
|
|
}
|
|
|
|
{ // Removing a constraint with 32 properties should succeed.
|
|
auto unique_acc = this->db_acc_->get()->UniqueAccess();
|
|
ASSERT_EQ(unique_acc->DropUniqueConstraint(this->label1, properties), UniqueConstraints::DeletionStatus::SUCCESS);
|
|
ASSERT_NO_ERROR(unique_acc->Commit());
|
|
}
|
|
{
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
ASSERT_TRUE(acc->ListAllConstraints().unique.empty());
|
|
ASSERT_NO_ERROR(acc->Commit());
|
|
}
|
|
}
|
|
|
|
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
|
/// TODO: andi consistency problems
|
|
TYPED_TEST(ConstraintsTest, UniqueConstraintsMultipleProperties) {
|
|
{
|
|
auto unique_acc = this->db_acc_->get()->UniqueAccess();
|
|
auto res = unique_acc->CreateUniqueConstraint(this->label1, {this->prop1, this->prop2});
|
|
ASSERT_TRUE(res.HasValue());
|
|
ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
|
|
ASSERT_NO_ERROR(unique_acc->Commit());
|
|
}
|
|
|
|
{
|
|
// An attempt to create an existing unique constraint.
|
|
auto unique_acc = this->db_acc_->get()->UniqueAccess();
|
|
auto res = unique_acc->CreateUniqueConstraint(this->label1, {this->prop2, this->prop1});
|
|
ASSERT_TRUE(res.HasValue());
|
|
ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::ALREADY_EXISTS);
|
|
ASSERT_NO_ERROR(unique_acc->Commit());
|
|
}
|
|
|
|
Gid gid1;
|
|
Gid gid2;
|
|
{
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
auto vertex1 = acc->CreateVertex();
|
|
auto vertex2 = acc->CreateVertex();
|
|
gid1 = vertex1.Gid();
|
|
gid2 = vertex2.Gid();
|
|
|
|
ASSERT_NO_ERROR(vertex1.AddLabel(this->label1));
|
|
ASSERT_NO_ERROR(vertex1.SetProperty(this->prop1, PropertyValue(1)));
|
|
ASSERT_NO_ERROR(vertex1.SetProperty(this->prop2, PropertyValue(2)));
|
|
|
|
ASSERT_NO_ERROR(vertex2.AddLabel(this->label1));
|
|
ASSERT_NO_ERROR(vertex2.SetProperty(this->prop1, PropertyValue(1)));
|
|
ASSERT_NO_ERROR(vertex2.SetProperty(this->prop2, PropertyValue(3)));
|
|
|
|
ASSERT_NO_ERROR(acc->Commit());
|
|
}
|
|
|
|
// Try to change property of the second vertex so it becomes the same as the
|
|
// first vertex-> It should fail.
|
|
{
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
auto vertex2 = acc->FindVertex(gid2, View::OLD);
|
|
ASSERT_NO_ERROR(vertex2->SetProperty(this->prop2, PropertyValue(2)));
|
|
auto res = acc->Commit();
|
|
ASSERT_TRUE(res.HasError());
|
|
EXPECT_EQ(std::get<ConstraintViolation>(res.GetError()),
|
|
(ConstraintViolation{ConstraintViolation::Type::UNIQUE, this->label1,
|
|
std::set<PropertyId>{this->prop1, this->prop2}}));
|
|
}
|
|
|
|
// Then change the second property of both vertex to null. Property values of
|
|
// both vertices should now be equal. However, this operation should succeed
|
|
// since null value is treated as non-existing property.
|
|
{
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
auto vertex1 = acc->FindVertex(gid1, View::OLD);
|
|
auto vertex2 = acc->FindVertex(gid2, View::OLD);
|
|
ASSERT_NO_ERROR(vertex1->SetProperty(this->prop2, PropertyValue()));
|
|
ASSERT_NO_ERROR(vertex2->SetProperty(this->prop2, PropertyValue()));
|
|
ASSERT_NO_ERROR(acc->Commit());
|
|
}
|
|
}
|
|
|
|
/// TODO: andi Test passes when ran alone but fails when all tests are run
|
|
TYPED_TEST(ConstraintsTest, UniqueConstraintsInsertAbortInsert) {
|
|
{
|
|
auto unique_acc = this->db_acc_->get()->UniqueAccess();
|
|
auto res = unique_acc->CreateUniqueConstraint(this->label1, {this->prop1, this->prop2});
|
|
ASSERT_TRUE(res.HasValue());
|
|
ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
|
|
ASSERT_NO_ERROR(unique_acc->Commit());
|
|
}
|
|
|
|
{
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
auto vertex = acc->CreateVertex();
|
|
ASSERT_NO_ERROR(vertex.AddLabel(this->label1));
|
|
ASSERT_NO_ERROR(vertex.SetProperty(this->prop1, PropertyValue(1)));
|
|
ASSERT_NO_ERROR(vertex.SetProperty(this->prop2, PropertyValue(2)));
|
|
acc->Abort();
|
|
}
|
|
|
|
{
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
auto vertex = acc->CreateVertex();
|
|
ASSERT_NO_ERROR(vertex.AddLabel(this->label1));
|
|
ASSERT_NO_ERROR(vertex.SetProperty(this->prop2, PropertyValue(2)));
|
|
ASSERT_NO_ERROR(vertex.SetProperty(this->prop1, PropertyValue(1)));
|
|
ASSERT_NO_ERROR(acc->Commit());
|
|
}
|
|
}
|
|
|
|
TYPED_TEST(ConstraintsTest, UniqueConstraintsInsertRemoveInsert) {
|
|
{
|
|
auto unique_acc = this->db_acc_->get()->UniqueAccess();
|
|
auto res = unique_acc->CreateUniqueConstraint(this->label1, {this->prop1, this->prop2});
|
|
ASSERT_TRUE(res.HasValue());
|
|
ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
|
|
ASSERT_NO_ERROR(unique_acc->Commit());
|
|
}
|
|
|
|
Gid gid;
|
|
{
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
auto vertex = acc->CreateVertex();
|
|
gid = vertex.Gid();
|
|
ASSERT_NO_ERROR(vertex.AddLabel(this->label1));
|
|
ASSERT_NO_ERROR(vertex.SetProperty(this->prop1, PropertyValue(1)));
|
|
ASSERT_NO_ERROR(vertex.SetProperty(this->prop2, PropertyValue(2)));
|
|
ASSERT_NO_ERROR(acc->Commit());
|
|
}
|
|
|
|
{
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
auto vertex = acc->FindVertex(gid, View::OLD);
|
|
ASSERT_NO_ERROR(acc->DeleteVertex(&*vertex));
|
|
ASSERT_NO_ERROR(acc->Commit());
|
|
}
|
|
|
|
{
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
auto vertex = acc->CreateVertex();
|
|
ASSERT_NO_ERROR(vertex.AddLabel(this->label1));
|
|
ASSERT_NO_ERROR(vertex.SetProperty(this->prop1, PropertyValue(1)));
|
|
ASSERT_NO_ERROR(vertex.SetProperty(this->prop2, PropertyValue(2)));
|
|
ASSERT_NO_ERROR(acc->Commit());
|
|
}
|
|
}
|
|
|
|
TYPED_TEST(ConstraintsTest, UniqueConstraintsInsertRemoveAbortInsert) {
|
|
{
|
|
auto unique_acc = this->db_acc_->get()->UniqueAccess();
|
|
auto res = unique_acc->CreateUniqueConstraint(this->label1, {this->prop1, this->prop2});
|
|
ASSERT_TRUE(res.HasValue());
|
|
ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
|
|
ASSERT_NO_ERROR(unique_acc->Commit());
|
|
}
|
|
|
|
Gid gid;
|
|
{
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
auto vertex = acc->CreateVertex();
|
|
gid = vertex.Gid();
|
|
ASSERT_NO_ERROR(vertex.AddLabel(this->label1));
|
|
ASSERT_NO_ERROR(vertex.SetProperty(this->prop1, PropertyValue(2)));
|
|
ASSERT_NO_ERROR(vertex.SetProperty(this->prop2, PropertyValue(1)));
|
|
ASSERT_NO_ERROR(acc->Commit());
|
|
}
|
|
|
|
{
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
auto vertex = acc->FindVertex(gid, View::OLD);
|
|
ASSERT_NO_ERROR(acc->DeleteVertex(&*vertex));
|
|
acc->Abort();
|
|
}
|
|
|
|
{
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
auto vertex = acc->CreateVertex();
|
|
ASSERT_NO_ERROR(vertex.AddLabel(this->label1));
|
|
ASSERT_NO_ERROR(vertex.SetProperty(this->prop2, PropertyValue(1)));
|
|
ASSERT_NO_ERROR(vertex.SetProperty(this->prop1, PropertyValue(2)));
|
|
|
|
auto res = acc->Commit();
|
|
ASSERT_TRUE(res.HasError());
|
|
EXPECT_EQ(
|
|
std::get<ConstraintViolation>(res.GetError()),
|
|
(ConstraintViolation{ConstraintViolation::Type::UNIQUE, this->label1, std::set{this->prop1, this->prop2}}));
|
|
}
|
|
}
|
|
|
|
TYPED_TEST(ConstraintsTest, UniqueConstraintsDeleteVertexSetProperty) {
|
|
{
|
|
auto unique_acc = this->db_acc_->get()->UniqueAccess();
|
|
auto res = unique_acc->CreateUniqueConstraint(this->label1, {this->prop1});
|
|
ASSERT_TRUE(res.HasValue());
|
|
ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
|
|
ASSERT_NO_ERROR(unique_acc->Commit());
|
|
}
|
|
|
|
Gid gid1;
|
|
Gid gid2;
|
|
{
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
auto vertex1 = acc->CreateVertex();
|
|
auto vertex2 = acc->CreateVertex();
|
|
gid1 = vertex1.Gid();
|
|
gid2 = vertex2.Gid();
|
|
|
|
ASSERT_NO_ERROR(vertex1.AddLabel(this->label1));
|
|
ASSERT_NO_ERROR(vertex2.AddLabel(this->label1));
|
|
ASSERT_NO_ERROR(vertex1.SetProperty(this->prop1, PropertyValue(1)));
|
|
ASSERT_NO_ERROR(vertex2.SetProperty(this->prop1, PropertyValue(2)));
|
|
|
|
ASSERT_NO_ERROR(acc->Commit());
|
|
}
|
|
|
|
{
|
|
auto acc1 = this->storage->Access(ReplicationRole::MAIN);
|
|
auto acc2 = this->storage->Access(ReplicationRole::MAIN);
|
|
auto vertex1 = acc1->FindVertex(gid1, View::OLD);
|
|
auto vertex2 = acc2->FindVertex(gid2, View::OLD);
|
|
|
|
ASSERT_NO_ERROR(acc2->DeleteVertex(&*vertex2));
|
|
ASSERT_NO_ERROR(vertex1->SetProperty(this->prop1, PropertyValue(2)));
|
|
|
|
auto res = acc1->Commit();
|
|
ASSERT_TRUE(res.HasError());
|
|
EXPECT_EQ(std::get<ConstraintViolation>(res.GetError()),
|
|
(ConstraintViolation{ConstraintViolation::Type::UNIQUE, this->label1, std::set{this->prop1}}));
|
|
|
|
ASSERT_NO_ERROR(acc2->Commit());
|
|
}
|
|
}
|
|
|
|
TYPED_TEST(ConstraintsTest, UniqueConstraintsInsertDropInsert) {
|
|
{
|
|
auto unique_acc = this->db_acc_->get()->UniqueAccess();
|
|
auto res = unique_acc->CreateUniqueConstraint(this->label1, {this->prop1, this->prop2});
|
|
ASSERT_TRUE(res.HasValue());
|
|
ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
|
|
ASSERT_NO_ERROR(unique_acc->Commit());
|
|
}
|
|
|
|
{
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
auto vertex = acc->CreateVertex();
|
|
ASSERT_NO_ERROR(vertex.AddLabel(this->label1));
|
|
ASSERT_NO_ERROR(vertex.SetProperty(this->prop1, PropertyValue(1)));
|
|
ASSERT_NO_ERROR(vertex.SetProperty(this->prop2, PropertyValue(2)));
|
|
ASSERT_NO_ERROR(acc->Commit());
|
|
}
|
|
|
|
{
|
|
auto unique_acc = this->db_acc_->get()->UniqueAccess();
|
|
ASSERT_EQ(unique_acc->DropUniqueConstraint(this->label1, {this->prop2, this->prop1}),
|
|
UniqueConstraints::DeletionStatus::SUCCESS);
|
|
ASSERT_NO_ERROR(unique_acc->Commit());
|
|
}
|
|
|
|
{
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
auto vertex = acc->CreateVertex();
|
|
ASSERT_NO_ERROR(vertex.AddLabel(this->label1));
|
|
ASSERT_NO_ERROR(vertex.SetProperty(this->prop2, PropertyValue(2)));
|
|
ASSERT_NO_ERROR(vertex.SetProperty(this->prop1, PropertyValue(1)));
|
|
ASSERT_NO_ERROR(acc->Commit());
|
|
}
|
|
}
|
|
|
|
TYPED_TEST(ConstraintsTest, UniqueConstraintsComparePropertyValues) {
|
|
// Purpose of this test is to make sure that extracted property values
|
|
// are correctly compared.
|
|
|
|
{
|
|
auto unique_acc = this->db_acc_->get()->UniqueAccess();
|
|
auto res = unique_acc->CreateUniqueConstraint(this->label1, {this->prop1, this->prop2});
|
|
ASSERT_TRUE(res.HasValue());
|
|
ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
|
|
ASSERT_NO_ERROR(unique_acc->Commit());
|
|
}
|
|
|
|
{
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
auto vertex = acc->CreateVertex();
|
|
ASSERT_NO_ERROR(vertex.AddLabel(this->label1));
|
|
ASSERT_NO_ERROR(vertex.SetProperty(this->prop1, PropertyValue(2)));
|
|
ASSERT_NO_ERROR(vertex.SetProperty(this->prop2, PropertyValue(1)));
|
|
ASSERT_NO_ERROR(acc->Commit());
|
|
}
|
|
|
|
{
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
auto vertex = acc->CreateVertex();
|
|
ASSERT_NO_ERROR(vertex.AddLabel(this->label1));
|
|
ASSERT_NO_ERROR(vertex.SetProperty(this->prop1, PropertyValue(1)));
|
|
ASSERT_NO_ERROR(vertex.SetProperty(this->prop2, PropertyValue(2)));
|
|
ASSERT_NO_ERROR(acc->Commit());
|
|
}
|
|
|
|
{
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
auto vertex = acc->CreateVertex();
|
|
ASSERT_NO_ERROR(vertex.AddLabel(this->label1));
|
|
ASSERT_NO_ERROR(vertex.SetProperty(this->prop2, PropertyValue(0)));
|
|
ASSERT_NO_ERROR(vertex.SetProperty(this->prop1, PropertyValue(3)));
|
|
ASSERT_NO_ERROR(acc->Commit());
|
|
}
|
|
}
|
|
|
|
TYPED_TEST(ConstraintsTest, UniqueConstraintsClearOldData) {
|
|
// Purpose of this test is to make sure that extracted property values
|
|
// are correctly compared.
|
|
|
|
if constexpr ((std::is_same_v<TypeParam, memgraph::storage::DiskStorage>)) {
|
|
auto *disk_constraints =
|
|
static_cast<memgraph::storage::DiskUniqueConstraints *>(this->storage->constraints_.unique_constraints_.get());
|
|
auto *tx_db = disk_constraints->GetRocksDBStorage()->db_;
|
|
|
|
{
|
|
auto unique_acc = this->db_acc_->get()->UniqueAccess();
|
|
auto res = unique_acc->CreateUniqueConstraint(this->label1, {this->prop1});
|
|
ASSERT_TRUE(res.HasValue());
|
|
ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
|
|
ASSERT_NO_ERROR(unique_acc->Commit());
|
|
}
|
|
|
|
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
|
auto vertex = acc->CreateVertex();
|
|
ASSERT_NO_ERROR(vertex.AddLabel(this->label1));
|
|
ASSERT_NO_ERROR(vertex.SetProperty(this->prop1, PropertyValue(2)));
|
|
ASSERT_NO_ERROR(acc->Commit());
|
|
|
|
ASSERT_EQ(disk_test_utils::GetRealNumberOfEntriesInRocksDB(tx_db), 1);
|
|
|
|
auto acc2 = this->storage->Access(ReplicationRole::MAIN);
|
|
auto vertex2 = acc2->FindVertex(vertex.Gid(), memgraph::storage::View::NEW).value();
|
|
ASSERT_TRUE(vertex2.SetProperty(this->prop1, memgraph::storage::PropertyValue(2)).HasValue());
|
|
ASSERT_FALSE(acc2->Commit().HasError());
|
|
|
|
ASSERT_EQ(disk_test_utils::GetRealNumberOfEntriesInRocksDB(tx_db), 1);
|
|
|
|
auto acc3 = this->storage->Access(ReplicationRole::MAIN);
|
|
auto vertex3 = acc3->FindVertex(vertex.Gid(), memgraph::storage::View::NEW).value();
|
|
ASSERT_TRUE(vertex3.SetProperty(this->prop1, memgraph::storage::PropertyValue(10)).HasValue());
|
|
ASSERT_FALSE(acc3->Commit().HasError());
|
|
|
|
ASSERT_EQ(disk_test_utils::GetRealNumberOfEntriesInRocksDB(tx_db), 1);
|
|
}
|
|
}
|