Merge pull request #637 from memgraph/tyler_remove_storage_v2_durability_test
Remove unit tests for storage v2
This commit is contained in:
commit
d198819bf1
@ -278,49 +278,6 @@ target_link_libraries(${test_prefix}utils_temporal mg-utils)
|
||||
add_unit_test(utils_histogram.cpp)
|
||||
target_link_libraries(${test_prefix}utils_histogram mg-utils)
|
||||
|
||||
# Test mg-storage-v2
|
||||
add_unit_test(commit_log_v2.cpp)
|
||||
target_link_libraries(${test_prefix}commit_log_v2 gflags mg-utils mg-storage-v2)
|
||||
|
||||
add_unit_test(property_value_v2.cpp)
|
||||
target_link_libraries(${test_prefix}property_value_v2 mg-storage-v2 mg-utils)
|
||||
|
||||
add_unit_test(storage_v2.cpp)
|
||||
target_link_libraries(${test_prefix}storage_v2 mg-storage-v2 storage_test_utils)
|
||||
|
||||
add_unit_test(storage_v2_constraints.cpp)
|
||||
target_link_libraries(${test_prefix}storage_v2_constraints mg-storage-v2)
|
||||
|
||||
add_unit_test(storage_v2_decoder_encoder.cpp)
|
||||
target_link_libraries(${test_prefix}storage_v2_decoder_encoder mg-storage-v2)
|
||||
|
||||
add_unit_test(storage_v2_durability.cpp)
|
||||
target_link_libraries(${test_prefix}storage_v2_durability mg-storage-v2)
|
||||
|
||||
add_unit_test(storage_v2_edge.cpp)
|
||||
target_link_libraries(${test_prefix}storage_v2_edge mg-storage-v2)
|
||||
|
||||
add_unit_test(storage_v2_gc.cpp)
|
||||
target_link_libraries(${test_prefix}storage_v2_gc mg-storage-v2)
|
||||
|
||||
add_unit_test(storage_v2_indices.cpp)
|
||||
target_link_libraries(${test_prefix}storage_v2_indices mg-storage-v2 mg-utils)
|
||||
|
||||
add_unit_test(storage_v2_name_id_mapper.cpp)
|
||||
target_link_libraries(${test_prefix}storage_v2_name_id_mapper mg-storage-v2)
|
||||
|
||||
add_unit_test(storage_v2_property_store.cpp)
|
||||
target_link_libraries(${test_prefix}storage_v2_property_store mg-storage-v2 fmt)
|
||||
|
||||
add_unit_test(storage_v2_wal_file.cpp)
|
||||
target_link_libraries(${test_prefix}storage_v2_wal_file mg-storage-v2 fmt)
|
||||
|
||||
add_unit_test(storage_v2_replication.cpp)
|
||||
target_link_libraries(${test_prefix}storage_v2_replication mg-storage-v2 fmt)
|
||||
|
||||
add_unit_test(storage_v2_isolation_level.cpp)
|
||||
target_link_libraries(${test_prefix}storage_v2_isolation_level mg-storage-v2)
|
||||
|
||||
# Test mg-storage-v3
|
||||
add_library(storage_v3_test_utils storage_v3_test_utils.cpp)
|
||||
target_link_libraries(storage_v3_test_utils mg-storage-v3)
|
||||
|
@ -1,81 +0,0 @@
|
||||
// Copyright 2022 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 "storage/v2/commit_log.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace {
|
||||
inline constexpr size_t ids_per_block = 8192 * 64;
|
||||
} // namespace
|
||||
|
||||
TEST(CommitLog, Simple) {
|
||||
memgraph::storage::CommitLog log;
|
||||
EXPECT_EQ(log.OldestActive(), 0);
|
||||
|
||||
log.MarkFinished(1);
|
||||
EXPECT_EQ(log.OldestActive(), 0);
|
||||
|
||||
log.MarkFinished(0);
|
||||
EXPECT_EQ(log.OldestActive(), 2);
|
||||
}
|
||||
|
||||
TEST(CommitLog, Fields) {
|
||||
memgraph::storage::CommitLog log;
|
||||
|
||||
for (uint64_t i = 0; i < 64; ++i) {
|
||||
log.MarkFinished(i);
|
||||
EXPECT_EQ(log.OldestActive(), i + 1);
|
||||
}
|
||||
|
||||
for (uint64_t i = 128; i < 192; ++i) {
|
||||
log.MarkFinished(i);
|
||||
EXPECT_EQ(log.OldestActive(), 64);
|
||||
}
|
||||
|
||||
for (uint64_t i = 64; i < 128; ++i) {
|
||||
log.MarkFinished(i);
|
||||
EXPECT_EQ(log.OldestActive(), i < 127 ? i + 1 : 192);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(CommitLog, Blocks) {
|
||||
memgraph::storage::CommitLog log;
|
||||
|
||||
for (uint64_t i = 0; i < ids_per_block; ++i) {
|
||||
log.MarkFinished(i);
|
||||
EXPECT_EQ(log.OldestActive(), i + 1);
|
||||
}
|
||||
|
||||
for (uint64_t i = ids_per_block * 2; i < ids_per_block * 3; ++i) {
|
||||
log.MarkFinished(i);
|
||||
EXPECT_EQ(log.OldestActive(), ids_per_block);
|
||||
}
|
||||
|
||||
for (uint64_t i = ids_per_block; i < ids_per_block; ++i) {
|
||||
log.MarkFinished(i);
|
||||
EXPECT_EQ(log.OldestActive(), i < ids_per_block - 1 ? i + 1 : ids_per_block * 3);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(CommitLog, TrackAfterInitialId) {
|
||||
const auto check_marking_ids = [](auto *log, auto current_oldest_active) {
|
||||
ASSERT_EQ(log->OldestActive(), current_oldest_active);
|
||||
log->MarkFinished(current_oldest_active);
|
||||
++current_oldest_active;
|
||||
ASSERT_EQ(log->OldestActive(), current_oldest_active);
|
||||
};
|
||||
|
||||
for (uint64_t i = 0; i < 2 * ids_per_block; ++i) {
|
||||
memgraph::storage::CommitLog log{i};
|
||||
check_marking_ids(&log, i);
|
||||
}
|
||||
}
|
@ -1,814 +0,0 @@
|
||||
// Copyright 2022 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 <gtest/gtest.h>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "storage/v2/property_value.hpp"
|
||||
#include "storage/v2/temporal.hpp"
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST(PropertyValue, Null) {
|
||||
memgraph::storage::PropertyValue pv;
|
||||
|
||||
ASSERT_EQ(pv.type(), memgraph::storage::PropertyValue::Type::Null);
|
||||
|
||||
ASSERT_TRUE(pv.IsNull());
|
||||
ASSERT_FALSE(pv.IsBool());
|
||||
ASSERT_FALSE(pv.IsInt());
|
||||
ASSERT_FALSE(pv.IsDouble());
|
||||
ASSERT_FALSE(pv.IsString());
|
||||
ASSERT_FALSE(pv.IsList());
|
||||
ASSERT_FALSE(pv.IsMap());
|
||||
|
||||
ASSERT_THROW(pv.ValueBool(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(pv.ValueInt(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(pv.ValueDouble(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(pv.ValueString(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(pv.ValueList(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(pv.ValueMap(), memgraph::storage::PropertyValueException);
|
||||
|
||||
const auto &cpv = pv;
|
||||
|
||||
ASSERT_THROW(cpv.ValueBool(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(cpv.ValueInt(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(cpv.ValueDouble(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(cpv.ValueString(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(cpv.ValueList(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(cpv.ValueMap(), memgraph::storage::PropertyValueException);
|
||||
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << pv.type();
|
||||
ASSERT_EQ(ss.str(), "null");
|
||||
}
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << pv;
|
||||
ASSERT_EQ(ss.str(), "null");
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST(PropertyValue, Bool) {
|
||||
memgraph::storage::PropertyValue pv(false);
|
||||
|
||||
ASSERT_EQ(pv.type(), memgraph::storage::PropertyValue::Type::Bool);
|
||||
|
||||
ASSERT_FALSE(pv.IsNull());
|
||||
ASSERT_TRUE(pv.IsBool());
|
||||
ASSERT_FALSE(pv.IsInt());
|
||||
ASSERT_FALSE(pv.IsDouble());
|
||||
ASSERT_FALSE(pv.IsString());
|
||||
ASSERT_FALSE(pv.IsList());
|
||||
ASSERT_FALSE(pv.IsMap());
|
||||
|
||||
ASSERT_EQ(pv.ValueBool(), false);
|
||||
ASSERT_THROW(pv.ValueInt(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(pv.ValueDouble(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(pv.ValueString(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(pv.ValueList(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(pv.ValueMap(), memgraph::storage::PropertyValueException);
|
||||
|
||||
const auto &cpv = pv;
|
||||
|
||||
ASSERT_EQ(cpv.ValueBool(), false);
|
||||
ASSERT_THROW(cpv.ValueInt(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(cpv.ValueDouble(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(cpv.ValueString(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(cpv.ValueList(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(cpv.ValueMap(), memgraph::storage::PropertyValueException);
|
||||
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << pv.type();
|
||||
ASSERT_EQ(ss.str(), "bool");
|
||||
}
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << pv;
|
||||
ASSERT_EQ(ss.str(), "false");
|
||||
}
|
||||
{
|
||||
memgraph::storage::PropertyValue pvtrue(true);
|
||||
std::stringstream ss;
|
||||
ss << pvtrue;
|
||||
ASSERT_EQ(ss.str(), "true");
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST(PropertyValue, Int) {
|
||||
memgraph::storage::PropertyValue pv(123L);
|
||||
|
||||
ASSERT_EQ(pv.type(), memgraph::storage::PropertyValue::Type::Int);
|
||||
|
||||
ASSERT_FALSE(pv.IsNull());
|
||||
ASSERT_FALSE(pv.IsBool());
|
||||
ASSERT_TRUE(pv.IsInt());
|
||||
ASSERT_FALSE(pv.IsDouble());
|
||||
ASSERT_FALSE(pv.IsString());
|
||||
ASSERT_FALSE(pv.IsList());
|
||||
ASSERT_FALSE(pv.IsMap());
|
||||
|
||||
ASSERT_THROW(pv.ValueBool(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_EQ(pv.ValueInt(), 123L);
|
||||
ASSERT_THROW(pv.ValueDouble(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(pv.ValueString(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(pv.ValueList(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(pv.ValueMap(), memgraph::storage::PropertyValueException);
|
||||
|
||||
const auto &cpv = pv;
|
||||
|
||||
ASSERT_THROW(cpv.ValueBool(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_EQ(cpv.ValueInt(), 123L);
|
||||
ASSERT_THROW(cpv.ValueDouble(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(cpv.ValueString(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(cpv.ValueList(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(cpv.ValueMap(), memgraph::storage::PropertyValueException);
|
||||
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << pv.type();
|
||||
ASSERT_EQ(ss.str(), "int");
|
||||
}
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << pv;
|
||||
ASSERT_EQ(ss.str(), "123");
|
||||
}
|
||||
|
||||
{
|
||||
memgraph::storage::PropertyValue pvint(123);
|
||||
ASSERT_EQ(pvint.type(), memgraph::storage::PropertyValue::Type::Int);
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST(PropertyValue, Double) {
|
||||
memgraph::storage::PropertyValue pv(123.5);
|
||||
|
||||
ASSERT_EQ(pv.type(), memgraph::storage::PropertyValue::Type::Double);
|
||||
|
||||
ASSERT_FALSE(pv.IsNull());
|
||||
ASSERT_FALSE(pv.IsBool());
|
||||
ASSERT_FALSE(pv.IsInt());
|
||||
ASSERT_TRUE(pv.IsDouble());
|
||||
ASSERT_FALSE(pv.IsString());
|
||||
ASSERT_FALSE(pv.IsList());
|
||||
ASSERT_FALSE(pv.IsMap());
|
||||
|
||||
ASSERT_THROW(pv.ValueBool(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(pv.ValueInt(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_EQ(pv.ValueDouble(), 123.5);
|
||||
ASSERT_THROW(pv.ValueString(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(pv.ValueList(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(pv.ValueMap(), memgraph::storage::PropertyValueException);
|
||||
|
||||
const auto &cpv = pv;
|
||||
|
||||
ASSERT_THROW(cpv.ValueBool(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(cpv.ValueInt(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_EQ(cpv.ValueDouble(), 123.5);
|
||||
ASSERT_THROW(cpv.ValueString(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(cpv.ValueList(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(cpv.ValueMap(), memgraph::storage::PropertyValueException);
|
||||
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << pv.type();
|
||||
ASSERT_EQ(ss.str(), "double");
|
||||
}
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << pv;
|
||||
ASSERT_EQ(ss.str(), "123.5");
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST(PropertyValue, StringCopy) {
|
||||
std::string str("nandare");
|
||||
memgraph::storage::PropertyValue pv(str);
|
||||
|
||||
ASSERT_EQ(str, "nandare");
|
||||
|
||||
ASSERT_EQ(pv.type(), memgraph::storage::PropertyValue::Type::String);
|
||||
|
||||
ASSERT_FALSE(pv.IsNull());
|
||||
ASSERT_FALSE(pv.IsBool());
|
||||
ASSERT_FALSE(pv.IsInt());
|
||||
ASSERT_FALSE(pv.IsDouble());
|
||||
ASSERT_TRUE(pv.IsString());
|
||||
ASSERT_FALSE(pv.IsList());
|
||||
ASSERT_FALSE(pv.IsMap());
|
||||
|
||||
ASSERT_THROW(pv.ValueBool(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(pv.ValueInt(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(pv.ValueDouble(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_EQ(pv.ValueString(), "nandare");
|
||||
ASSERT_THROW(pv.ValueList(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(pv.ValueMap(), memgraph::storage::PropertyValueException);
|
||||
|
||||
const auto &cpv = pv;
|
||||
|
||||
ASSERT_THROW(cpv.ValueBool(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(cpv.ValueInt(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(cpv.ValueDouble(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_EQ(cpv.ValueString(), "nandare");
|
||||
ASSERT_THROW(cpv.ValueList(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(cpv.ValueMap(), memgraph::storage::PropertyValueException);
|
||||
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << pv.type();
|
||||
ASSERT_EQ(ss.str(), "string");
|
||||
}
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << pv;
|
||||
ASSERT_EQ(ss.str(), "nandare");
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST(PropertyValue, StringMove) {
|
||||
std::string str("nandare");
|
||||
memgraph::storage::PropertyValue pv(std::move(str));
|
||||
|
||||
ASSERT_EQ(str, "");
|
||||
|
||||
ASSERT_EQ(pv.type(), memgraph::storage::PropertyValue::Type::String);
|
||||
|
||||
ASSERT_FALSE(pv.IsNull());
|
||||
ASSERT_FALSE(pv.IsBool());
|
||||
ASSERT_FALSE(pv.IsInt());
|
||||
ASSERT_FALSE(pv.IsDouble());
|
||||
ASSERT_TRUE(pv.IsString());
|
||||
ASSERT_FALSE(pv.IsList());
|
||||
ASSERT_FALSE(pv.IsMap());
|
||||
|
||||
ASSERT_THROW(pv.ValueBool(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(pv.ValueInt(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(pv.ValueDouble(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_EQ(pv.ValueString(), "nandare");
|
||||
ASSERT_THROW(pv.ValueList(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(pv.ValueMap(), memgraph::storage::PropertyValueException);
|
||||
|
||||
const auto &cpv = pv;
|
||||
|
||||
ASSERT_THROW(cpv.ValueBool(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(cpv.ValueInt(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(cpv.ValueDouble(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_EQ(cpv.ValueString(), "nandare");
|
||||
ASSERT_THROW(cpv.ValueList(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(cpv.ValueMap(), memgraph::storage::PropertyValueException);
|
||||
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << pv.type();
|
||||
ASSERT_EQ(ss.str(), "string");
|
||||
}
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << pv;
|
||||
ASSERT_EQ(ss.str(), "nandare");
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST(PropertyValue, ListCopy) {
|
||||
std::vector<memgraph::storage::PropertyValue> vec{memgraph::storage::PropertyValue("nandare"),
|
||||
memgraph::storage::PropertyValue(123)};
|
||||
memgraph::storage::PropertyValue pv(vec);
|
||||
|
||||
ASSERT_EQ(vec.size(), 2);
|
||||
ASSERT_EQ(vec[0].ValueString(), "nandare");
|
||||
ASSERT_EQ(vec[1].ValueInt(), 123);
|
||||
|
||||
ASSERT_EQ(pv.type(), memgraph::storage::PropertyValue::Type::List);
|
||||
|
||||
ASSERT_FALSE(pv.IsNull());
|
||||
ASSERT_FALSE(pv.IsBool());
|
||||
ASSERT_FALSE(pv.IsInt());
|
||||
ASSERT_FALSE(pv.IsDouble());
|
||||
ASSERT_FALSE(pv.IsString());
|
||||
ASSERT_TRUE(pv.IsList());
|
||||
ASSERT_FALSE(pv.IsMap());
|
||||
|
||||
ASSERT_THROW(pv.ValueBool(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(pv.ValueInt(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(pv.ValueDouble(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(pv.ValueString(), memgraph::storage::PropertyValueException);
|
||||
{
|
||||
const auto &ret = pv.ValueList();
|
||||
ASSERT_EQ(ret.size(), 2);
|
||||
ASSERT_EQ(ret[0].ValueString(), "nandare");
|
||||
ASSERT_EQ(ret[1].ValueInt(), 123);
|
||||
}
|
||||
ASSERT_THROW(pv.ValueMap(), memgraph::storage::PropertyValueException);
|
||||
|
||||
const auto &cpv = pv;
|
||||
|
||||
ASSERT_THROW(cpv.ValueBool(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(cpv.ValueInt(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(cpv.ValueDouble(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(cpv.ValueString(), memgraph::storage::PropertyValueException);
|
||||
{
|
||||
const auto &ret = cpv.ValueList();
|
||||
ASSERT_EQ(ret.size(), 2);
|
||||
ASSERT_EQ(ret[0].ValueString(), "nandare");
|
||||
ASSERT_EQ(ret[1].ValueInt(), 123);
|
||||
}
|
||||
ASSERT_THROW(cpv.ValueMap(), memgraph::storage::PropertyValueException);
|
||||
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << pv.type();
|
||||
ASSERT_EQ(ss.str(), "list");
|
||||
}
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << pv;
|
||||
ASSERT_EQ(ss.str(), "[nandare, 123]");
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST(PropertyValue, ListMove) {
|
||||
std::vector<memgraph::storage::PropertyValue> vec{memgraph::storage::PropertyValue("nandare"),
|
||||
memgraph::storage::PropertyValue(123)};
|
||||
memgraph::storage::PropertyValue pv(std::move(vec));
|
||||
|
||||
ASSERT_EQ(vec.size(), 0);
|
||||
|
||||
ASSERT_EQ(pv.type(), memgraph::storage::PropertyValue::Type::List);
|
||||
|
||||
ASSERT_FALSE(pv.IsNull());
|
||||
ASSERT_FALSE(pv.IsBool());
|
||||
ASSERT_FALSE(pv.IsInt());
|
||||
ASSERT_FALSE(pv.IsDouble());
|
||||
ASSERT_FALSE(pv.IsString());
|
||||
ASSERT_TRUE(pv.IsList());
|
||||
ASSERT_FALSE(pv.IsMap());
|
||||
|
||||
ASSERT_THROW(pv.ValueBool(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(pv.ValueInt(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(pv.ValueDouble(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(pv.ValueString(), memgraph::storage::PropertyValueException);
|
||||
{
|
||||
const auto &ret = pv.ValueList();
|
||||
ASSERT_EQ(ret.size(), 2);
|
||||
ASSERT_EQ(ret[0].ValueString(), "nandare");
|
||||
ASSERT_EQ(ret[1].ValueInt(), 123);
|
||||
}
|
||||
ASSERT_THROW(pv.ValueMap(), memgraph::storage::PropertyValueException);
|
||||
|
||||
const auto &cpv = pv;
|
||||
|
||||
ASSERT_THROW(cpv.ValueBool(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(cpv.ValueInt(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(cpv.ValueDouble(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(cpv.ValueString(), memgraph::storage::PropertyValueException);
|
||||
{
|
||||
const auto &ret = cpv.ValueList();
|
||||
ASSERT_EQ(ret.size(), 2);
|
||||
ASSERT_EQ(ret[0].ValueString(), "nandare");
|
||||
ASSERT_EQ(ret[1].ValueInt(), 123);
|
||||
}
|
||||
ASSERT_THROW(cpv.ValueMap(), memgraph::storage::PropertyValueException);
|
||||
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << pv.type();
|
||||
ASSERT_EQ(ss.str(), "list");
|
||||
}
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << pv;
|
||||
ASSERT_EQ(ss.str(), "[nandare, 123]");
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST(PropertyValue, MapCopy) {
|
||||
std::map<std::string, memgraph::storage::PropertyValue> map{{"nandare", memgraph::storage::PropertyValue(123)}};
|
||||
memgraph::storage::PropertyValue pv(map);
|
||||
|
||||
ASSERT_EQ(map.size(), 1);
|
||||
ASSERT_EQ(map.at("nandare").ValueInt(), 123);
|
||||
|
||||
ASSERT_EQ(pv.type(), memgraph::storage::PropertyValue::Type::Map);
|
||||
|
||||
ASSERT_FALSE(pv.IsNull());
|
||||
ASSERT_FALSE(pv.IsBool());
|
||||
ASSERT_FALSE(pv.IsInt());
|
||||
ASSERT_FALSE(pv.IsDouble());
|
||||
ASSERT_FALSE(pv.IsString());
|
||||
ASSERT_FALSE(pv.IsList());
|
||||
ASSERT_TRUE(pv.IsMap());
|
||||
|
||||
ASSERT_THROW(pv.ValueBool(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(pv.ValueInt(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(pv.ValueDouble(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(pv.ValueString(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(pv.ValueList(), memgraph::storage::PropertyValueException);
|
||||
{
|
||||
const auto &ret = pv.ValueMap();
|
||||
ASSERT_EQ(ret.size(), 1);
|
||||
ASSERT_EQ(ret.at("nandare").ValueInt(), 123);
|
||||
}
|
||||
|
||||
const auto &cpv = pv;
|
||||
|
||||
ASSERT_THROW(cpv.ValueBool(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(cpv.ValueInt(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(cpv.ValueDouble(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(cpv.ValueString(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(cpv.ValueList(), memgraph::storage::PropertyValueException);
|
||||
{
|
||||
const auto &ret = cpv.ValueMap();
|
||||
ASSERT_EQ(ret.size(), 1);
|
||||
ASSERT_EQ(ret.at("nandare").ValueInt(), 123);
|
||||
}
|
||||
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << pv.type();
|
||||
ASSERT_EQ(ss.str(), "map");
|
||||
}
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << pv;
|
||||
ASSERT_EQ(ss.str(), "{nandare: 123}");
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST(PropertyValue, MapMove) {
|
||||
std::map<std::string, memgraph::storage::PropertyValue> map{{"nandare", memgraph::storage::PropertyValue(123)}};
|
||||
memgraph::storage::PropertyValue pv(std::move(map));
|
||||
|
||||
ASSERT_EQ(map.size(), 0);
|
||||
|
||||
ASSERT_EQ(pv.type(), memgraph::storage::PropertyValue::Type::Map);
|
||||
|
||||
ASSERT_FALSE(pv.IsNull());
|
||||
ASSERT_FALSE(pv.IsBool());
|
||||
ASSERT_FALSE(pv.IsInt());
|
||||
ASSERT_FALSE(pv.IsDouble());
|
||||
ASSERT_FALSE(pv.IsString());
|
||||
ASSERT_FALSE(pv.IsList());
|
||||
ASSERT_TRUE(pv.IsMap());
|
||||
|
||||
ASSERT_THROW(pv.ValueBool(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(pv.ValueInt(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(pv.ValueDouble(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(pv.ValueString(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(pv.ValueList(), memgraph::storage::PropertyValueException);
|
||||
{
|
||||
const auto &ret = pv.ValueMap();
|
||||
ASSERT_EQ(ret.size(), 1);
|
||||
ASSERT_EQ(ret.at("nandare").ValueInt(), 123);
|
||||
}
|
||||
|
||||
const auto &cpv = pv;
|
||||
|
||||
ASSERT_THROW(cpv.ValueBool(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(cpv.ValueInt(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(cpv.ValueDouble(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(cpv.ValueString(), memgraph::storage::PropertyValueException);
|
||||
ASSERT_THROW(cpv.ValueList(), memgraph::storage::PropertyValueException);
|
||||
{
|
||||
const auto &ret = cpv.ValueMap();
|
||||
ASSERT_EQ(ret.size(), 1);
|
||||
ASSERT_EQ(ret.at("nandare").ValueInt(), 123);
|
||||
}
|
||||
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << pv.type();
|
||||
ASSERT_EQ(ss.str(), "map");
|
||||
}
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << pv;
|
||||
ASSERT_EQ(ss.str(), "{nandare: 123}");
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST(PropertyValue, CopyConstructor) {
|
||||
std::vector<memgraph::storage::PropertyValue> vec{memgraph::storage::PropertyValue(true),
|
||||
memgraph::storage::PropertyValue(123)};
|
||||
std::map<std::string, memgraph::storage::PropertyValue> map{{"nandare", memgraph::storage::PropertyValue(false)}};
|
||||
std::vector<memgraph::storage::PropertyValue> data{
|
||||
memgraph::storage::PropertyValue(),
|
||||
memgraph::storage::PropertyValue(true),
|
||||
memgraph::storage::PropertyValue(123),
|
||||
memgraph::storage::PropertyValue(123.5),
|
||||
memgraph::storage::PropertyValue("nandare"),
|
||||
memgraph::storage::PropertyValue(vec),
|
||||
memgraph::storage::PropertyValue(map),
|
||||
memgraph::storage::PropertyValue(memgraph::storage::TemporalData(memgraph::storage::TemporalType::Date, 23))};
|
||||
|
||||
for (const auto &item : data) {
|
||||
memgraph::storage::PropertyValue pv(item);
|
||||
ASSERT_EQ(pv.type(), item.type());
|
||||
switch (item.type()) {
|
||||
case memgraph::storage::PropertyValue::Type::Null:
|
||||
ASSERT_TRUE(pv.IsNull());
|
||||
break;
|
||||
case memgraph::storage::PropertyValue::Type::Bool:
|
||||
ASSERT_EQ(pv.ValueBool(), item.ValueBool());
|
||||
break;
|
||||
case memgraph::storage::PropertyValue::Type::Int:
|
||||
ASSERT_EQ(pv.ValueInt(), item.ValueInt());
|
||||
break;
|
||||
case memgraph::storage::PropertyValue::Type::Double:
|
||||
ASSERT_EQ(pv.ValueDouble(), item.ValueDouble());
|
||||
break;
|
||||
case memgraph::storage::PropertyValue::Type::String:
|
||||
ASSERT_EQ(pv.ValueString(), item.ValueString());
|
||||
break;
|
||||
case memgraph::storage::PropertyValue::Type::List:
|
||||
ASSERT_EQ(pv.ValueList(), item.ValueList());
|
||||
break;
|
||||
case memgraph::storage::PropertyValue::Type::Map:
|
||||
ASSERT_EQ(pv.ValueMap(), item.ValueMap());
|
||||
break;
|
||||
case memgraph::storage::PropertyValue::Type::TemporalData:
|
||||
ASSERT_EQ(pv.ValueTemporalData(), item.ValueTemporalData());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST(PropertyValue, MoveConstructor) {
|
||||
std::vector<memgraph::storage::PropertyValue> vec{memgraph::storage::PropertyValue(true),
|
||||
memgraph::storage::PropertyValue(123)};
|
||||
std::map<std::string, memgraph::storage::PropertyValue> map{{"nandare", memgraph::storage::PropertyValue(false)}};
|
||||
std::vector<memgraph::storage::PropertyValue> data{
|
||||
memgraph::storage::PropertyValue(),
|
||||
memgraph::storage::PropertyValue(true),
|
||||
memgraph::storage::PropertyValue(123),
|
||||
memgraph::storage::PropertyValue(123.5),
|
||||
memgraph::storage::PropertyValue("nandare"),
|
||||
memgraph::storage::PropertyValue(vec),
|
||||
memgraph::storage::PropertyValue(map),
|
||||
memgraph::storage::PropertyValue(memgraph::storage::TemporalData(memgraph::storage::TemporalType::Date, 23))};
|
||||
|
||||
for (auto &item : data) {
|
||||
memgraph::storage::PropertyValue copy(item);
|
||||
memgraph::storage::PropertyValue pv(std::move(item));
|
||||
ASSERT_EQ(item.type(), memgraph::storage::PropertyValue::Type::Null);
|
||||
ASSERT_EQ(pv.type(), copy.type());
|
||||
switch (copy.type()) {
|
||||
case memgraph::storage::PropertyValue::Type::Null:
|
||||
ASSERT_TRUE(pv.IsNull());
|
||||
break;
|
||||
case memgraph::storage::PropertyValue::Type::Bool:
|
||||
ASSERT_EQ(pv.ValueBool(), copy.ValueBool());
|
||||
break;
|
||||
case memgraph::storage::PropertyValue::Type::Int:
|
||||
ASSERT_EQ(pv.ValueInt(), copy.ValueInt());
|
||||
break;
|
||||
case memgraph::storage::PropertyValue::Type::Double:
|
||||
ASSERT_EQ(pv.ValueDouble(), copy.ValueDouble());
|
||||
break;
|
||||
case memgraph::storage::PropertyValue::Type::String:
|
||||
ASSERT_EQ(pv.ValueString(), copy.ValueString());
|
||||
break;
|
||||
case memgraph::storage::PropertyValue::Type::List:
|
||||
ASSERT_EQ(pv.ValueList(), copy.ValueList());
|
||||
break;
|
||||
case memgraph::storage::PropertyValue::Type::Map:
|
||||
ASSERT_EQ(pv.ValueMap(), copy.ValueMap());
|
||||
break;
|
||||
case memgraph::storage::PropertyValue::Type::TemporalData:
|
||||
ASSERT_EQ(pv.ValueTemporalData(), copy.ValueTemporalData());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST(PropertyValue, CopyAssignment) {
|
||||
std::vector<memgraph::storage::PropertyValue> vec{memgraph::storage::PropertyValue(true),
|
||||
memgraph::storage::PropertyValue(123)};
|
||||
std::map<std::string, memgraph::storage::PropertyValue> map{{"nandare", memgraph::storage::PropertyValue(false)}};
|
||||
std::vector<memgraph::storage::PropertyValue> data{
|
||||
memgraph::storage::PropertyValue(),
|
||||
memgraph::storage::PropertyValue(true),
|
||||
memgraph::storage::PropertyValue(123),
|
||||
memgraph::storage::PropertyValue(123.5),
|
||||
memgraph::storage::PropertyValue("nandare"),
|
||||
memgraph::storage::PropertyValue(vec),
|
||||
memgraph::storage::PropertyValue(map),
|
||||
memgraph::storage::PropertyValue(memgraph::storage::TemporalData(memgraph::storage::TemporalType::Date, 23))};
|
||||
|
||||
for (const auto &item : data) {
|
||||
memgraph::storage::PropertyValue pv(123);
|
||||
pv = item;
|
||||
ASSERT_EQ(pv.type(), item.type());
|
||||
switch (item.type()) {
|
||||
case memgraph::storage::PropertyValue::Type::Null:
|
||||
ASSERT_TRUE(pv.IsNull());
|
||||
break;
|
||||
case memgraph::storage::PropertyValue::Type::Bool:
|
||||
ASSERT_EQ(pv.ValueBool(), item.ValueBool());
|
||||
break;
|
||||
case memgraph::storage::PropertyValue::Type::Int:
|
||||
ASSERT_EQ(pv.ValueInt(), item.ValueInt());
|
||||
break;
|
||||
case memgraph::storage::PropertyValue::Type::Double:
|
||||
ASSERT_EQ(pv.ValueDouble(), item.ValueDouble());
|
||||
break;
|
||||
case memgraph::storage::PropertyValue::Type::String:
|
||||
ASSERT_EQ(pv.ValueString(), item.ValueString());
|
||||
break;
|
||||
case memgraph::storage::PropertyValue::Type::List:
|
||||
ASSERT_EQ(pv.ValueList(), item.ValueList());
|
||||
break;
|
||||
case memgraph::storage::PropertyValue::Type::Map:
|
||||
ASSERT_EQ(pv.ValueMap(), item.ValueMap());
|
||||
break;
|
||||
case memgraph::storage::PropertyValue::Type::TemporalData:
|
||||
ASSERT_EQ(pv.ValueTemporalData(), item.ValueTemporalData());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST(PropertyValue, MoveAssignment) {
|
||||
std::vector<memgraph::storage::PropertyValue> vec{memgraph::storage::PropertyValue(true),
|
||||
memgraph::storage::PropertyValue(123)};
|
||||
std::map<std::string, memgraph::storage::PropertyValue> map{{"nandare", memgraph::storage::PropertyValue(false)}};
|
||||
std::vector<memgraph::storage::PropertyValue> data{
|
||||
memgraph::storage::PropertyValue(),
|
||||
memgraph::storage::PropertyValue(true),
|
||||
memgraph::storage::PropertyValue(123),
|
||||
memgraph::storage::PropertyValue(123.5),
|
||||
memgraph::storage::PropertyValue("nandare"),
|
||||
memgraph::storage::PropertyValue(vec),
|
||||
memgraph::storage::PropertyValue(map),
|
||||
memgraph::storage::PropertyValue(memgraph::storage::TemporalData(memgraph::storage::TemporalType::Date, 23))};
|
||||
|
||||
for (auto &item : data) {
|
||||
memgraph::storage::PropertyValue copy(item);
|
||||
memgraph::storage::PropertyValue pv(123);
|
||||
pv = std::move(item);
|
||||
ASSERT_EQ(item.type(), memgraph::storage::PropertyValue::Type::Null);
|
||||
ASSERT_EQ(pv.type(), copy.type());
|
||||
switch (copy.type()) {
|
||||
case memgraph::storage::PropertyValue::Type::Null:
|
||||
ASSERT_TRUE(pv.IsNull());
|
||||
break;
|
||||
case memgraph::storage::PropertyValue::Type::Bool:
|
||||
ASSERT_EQ(pv.ValueBool(), copy.ValueBool());
|
||||
break;
|
||||
case memgraph::storage::PropertyValue::Type::Int:
|
||||
ASSERT_EQ(pv.ValueInt(), copy.ValueInt());
|
||||
break;
|
||||
case memgraph::storage::PropertyValue::Type::Double:
|
||||
ASSERT_EQ(pv.ValueDouble(), copy.ValueDouble());
|
||||
break;
|
||||
case memgraph::storage::PropertyValue::Type::String:
|
||||
ASSERT_EQ(pv.ValueString(), copy.ValueString());
|
||||
break;
|
||||
case memgraph::storage::PropertyValue::Type::List:
|
||||
ASSERT_EQ(pv.ValueList(), copy.ValueList());
|
||||
break;
|
||||
case memgraph::storage::PropertyValue::Type::Map:
|
||||
ASSERT_EQ(pv.ValueMap(), copy.ValueMap());
|
||||
break;
|
||||
case memgraph::storage::PropertyValue::Type::TemporalData:
|
||||
ASSERT_EQ(pv.ValueTemporalData(), copy.ValueTemporalData());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST(PropertyValue, CopyAssignmentSelf) {
|
||||
memgraph::storage::PropertyValue pv("nandare");
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wself-assign-overloaded"
|
||||
pv = pv;
|
||||
#pragma clang diagnostic pop
|
||||
ASSERT_EQ(pv.type(), memgraph::storage::PropertyValue::Type::String);
|
||||
ASSERT_EQ(pv.ValueString(), "nandare");
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST(PropertyValue, MoveAssignmentSelf) {
|
||||
memgraph::storage::PropertyValue pv("nandare");
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wself-move"
|
||||
pv = std::move(pv);
|
||||
#pragma clang diagnostic pop
|
||||
ASSERT_EQ(pv.type(), memgraph::storage::PropertyValue::Type::String);
|
||||
ASSERT_EQ(pv.ValueString(), "nandare");
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST(PropertyValue, Equal) {
|
||||
std::vector<memgraph::storage::PropertyValue> vec{memgraph::storage::PropertyValue(true),
|
||||
memgraph::storage::PropertyValue(123)};
|
||||
std::map<std::string, memgraph::storage::PropertyValue> map{{"nandare", memgraph::storage::PropertyValue(false)}};
|
||||
std::vector<memgraph::storage::PropertyValue> data{
|
||||
memgraph::storage::PropertyValue(), memgraph::storage::PropertyValue(true),
|
||||
memgraph::storage::PropertyValue(123), memgraph::storage::PropertyValue(123.5),
|
||||
memgraph::storage::PropertyValue("nandare"), memgraph::storage::PropertyValue(vec),
|
||||
memgraph::storage::PropertyValue(map)};
|
||||
for (const auto &item1 : data) {
|
||||
for (const auto &item2 : data) {
|
||||
if (item1.type() == item2.type()) {
|
||||
ASSERT_TRUE(item1 == item2);
|
||||
} else {
|
||||
ASSERT_FALSE(item1 == item2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST(PropertyValue, Less) {
|
||||
std::vector<memgraph::storage::PropertyValue> vec{memgraph::storage::PropertyValue(true),
|
||||
memgraph::storage::PropertyValue(123)};
|
||||
std::map<std::string, memgraph::storage::PropertyValue> map{{"nandare", memgraph::storage::PropertyValue(false)}};
|
||||
std::vector<memgraph::storage::PropertyValue> data{
|
||||
memgraph::storage::PropertyValue(), memgraph::storage::PropertyValue(true),
|
||||
memgraph::storage::PropertyValue(123), memgraph::storage::PropertyValue(123.5),
|
||||
memgraph::storage::PropertyValue("nandare"), memgraph::storage::PropertyValue(vec),
|
||||
memgraph::storage::PropertyValue(map)};
|
||||
for (size_t i = 0; i < data.size(); ++i) {
|
||||
for (size_t j = 0; j < data.size(); ++j) {
|
||||
auto item1 = data[i];
|
||||
auto item2 = data[j];
|
||||
if (i < j) {
|
||||
ASSERT_TRUE(item1 < item2);
|
||||
} else {
|
||||
ASSERT_FALSE(item1 < item2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(PropertyValue, NumeralTypesComparison) {
|
||||
auto v_int = memgraph::storage::PropertyValue(2);
|
||||
auto v_double = memgraph::storage::PropertyValue(2.0);
|
||||
ASSERT_TRUE(v_int.IsInt());
|
||||
ASSERT_TRUE(v_double.IsDouble());
|
||||
ASSERT_TRUE(v_int == v_double);
|
||||
ASSERT_FALSE(v_int < v_double);
|
||||
ASSERT_FALSE(v_double < v_int);
|
||||
}
|
||||
|
||||
TEST(PropertyValue, NestedNumeralTypesComparison) {
|
||||
auto v1 = memgraph::storage::PropertyValue(
|
||||
std::vector<memgraph::storage::PropertyValue>{memgraph::storage::PropertyValue(1)});
|
||||
auto v2 = memgraph::storage::PropertyValue(
|
||||
std::vector<memgraph::storage::PropertyValue>{memgraph::storage::PropertyValue(1.5)});
|
||||
auto v3 = memgraph::storage::PropertyValue(
|
||||
std::vector<memgraph::storage::PropertyValue>{memgraph::storage::PropertyValue(2)});
|
||||
|
||||
auto v1alt = memgraph::storage::PropertyValue(
|
||||
std::vector<memgraph::storage::PropertyValue>{memgraph::storage::PropertyValue(1.0)});
|
||||
auto v3alt = memgraph::storage::PropertyValue(
|
||||
std::vector<memgraph::storage::PropertyValue>{memgraph::storage::PropertyValue(2.0)});
|
||||
|
||||
ASSERT_TRUE(v1 == v1alt);
|
||||
ASSERT_TRUE(v3 == v3alt);
|
||||
|
||||
ASSERT_FALSE(v1 == v2);
|
||||
ASSERT_FALSE(v2 == v1);
|
||||
ASSERT_FALSE(v2 == v3);
|
||||
ASSERT_FALSE(v3 == v2);
|
||||
ASSERT_FALSE(v1 == v3);
|
||||
ASSERT_FALSE(v3 == v1);
|
||||
|
||||
ASSERT_TRUE(v1 < v2);
|
||||
ASSERT_TRUE(v2 < v3);
|
||||
ASSERT_TRUE(v1 < v3);
|
||||
ASSERT_FALSE(v2 < v1);
|
||||
ASSERT_FALSE(v3 < v2);
|
||||
ASSERT_FALSE(v3 < v1);
|
||||
|
||||
ASSERT_TRUE(v1alt < v2);
|
||||
ASSERT_TRUE(v2 < v3alt);
|
||||
ASSERT_TRUE(v1alt < v3alt);
|
||||
ASSERT_FALSE(v2 < v1alt);
|
||||
ASSERT_FALSE(v3alt < v2);
|
||||
ASSERT_FALSE(v3 < v1alt);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,973 +0,0 @@
|
||||
// Copyright 2022 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 "storage/v2/storage.hpp"
|
||||
|
||||
// NOLINTNEXTLINE(google-build-using-namespace)
|
||||
using namespace memgraph::storage;
|
||||
|
||||
using testing::UnorderedElementsAre;
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
|
||||
#define ASSERT_NO_ERROR(result) ASSERT_FALSE((result).HasError())
|
||||
|
||||
class ConstraintsTest : public testing::Test {
|
||||
protected:
|
||||
ConstraintsTest()
|
||||
: prop1(storage.NameToProperty("prop1")),
|
||||
prop2(storage.NameToProperty("prop2")),
|
||||
label1(storage.NameToLabel("label1")),
|
||||
label2(storage.NameToLabel("label2")) {}
|
||||
|
||||
Storage storage;
|
||||
PropertyId prop1;
|
||||
PropertyId prop2;
|
||||
LabelId label1;
|
||||
LabelId label2;
|
||||
};
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST_F(ConstraintsTest, ExistenceConstraintsCreateAndDrop) {
|
||||
EXPECT_EQ(storage.ListAllConstraints().existence.size(), 0);
|
||||
{
|
||||
auto res = storage.CreateExistenceConstraint(label1, prop1);
|
||||
EXPECT_TRUE(res.HasValue() && res.GetValue());
|
||||
}
|
||||
EXPECT_THAT(storage.ListAllConstraints().existence, UnorderedElementsAre(std::make_pair(label1, prop1)));
|
||||
{
|
||||
auto res = storage.CreateExistenceConstraint(label1, prop1);
|
||||
EXPECT_TRUE(res.HasValue() && !res.GetValue());
|
||||
}
|
||||
EXPECT_THAT(storage.ListAllConstraints().existence, UnorderedElementsAre(std::make_pair(label1, prop1)));
|
||||
{
|
||||
auto res = storage.CreateExistenceConstraint(label2, prop1);
|
||||
EXPECT_TRUE(res.HasValue() && res.GetValue());
|
||||
}
|
||||
EXPECT_THAT(storage.ListAllConstraints().existence,
|
||||
UnorderedElementsAre(std::make_pair(label1, prop1), std::make_pair(label2, prop1)));
|
||||
EXPECT_TRUE(storage.DropExistenceConstraint(label1, prop1));
|
||||
EXPECT_FALSE(storage.DropExistenceConstraint(label1, prop1));
|
||||
EXPECT_THAT(storage.ListAllConstraints().existence, UnorderedElementsAre(std::make_pair(label2, prop1)));
|
||||
EXPECT_TRUE(storage.DropExistenceConstraint(label2, prop1));
|
||||
EXPECT_FALSE(storage.DropExistenceConstraint(label2, prop2));
|
||||
EXPECT_EQ(storage.ListAllConstraints().existence.size(), 0);
|
||||
{
|
||||
auto res = storage.CreateExistenceConstraint(label2, prop1);
|
||||
EXPECT_TRUE(res.HasValue() && res.GetValue());
|
||||
}
|
||||
EXPECT_THAT(storage.ListAllConstraints().existence, UnorderedElementsAre(std::make_pair(label2, prop1)));
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST_F(ConstraintsTest, ExistenceConstraintsCreateFailure1) {
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
auto vertex = acc.CreateVertex();
|
||||
ASSERT_NO_ERROR(vertex.AddLabel(label1));
|
||||
ASSERT_NO_ERROR(acc.Commit());
|
||||
}
|
||||
{
|
||||
auto res = storage.CreateExistenceConstraint(label1, prop1);
|
||||
ASSERT_TRUE(res.HasError());
|
||||
EXPECT_EQ(res.GetError(),
|
||||
(ConstraintViolation{ConstraintViolation::Type::EXISTENCE, label1, std::set<PropertyId>{prop1}}));
|
||||
}
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
for (auto vertex : acc.Vertices(View::OLD)) {
|
||||
ASSERT_NO_ERROR(acc.DeleteVertex(&vertex));
|
||||
}
|
||||
ASSERT_NO_ERROR(acc.Commit());
|
||||
}
|
||||
{
|
||||
auto res = storage.CreateExistenceConstraint(label1, prop1);
|
||||
EXPECT_TRUE(res.HasValue() && res.GetValue());
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST_F(ConstraintsTest, ExistenceConstraintsCreateFailure2) {
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
auto vertex = acc.CreateVertex();
|
||||
ASSERT_NO_ERROR(vertex.AddLabel(label1));
|
||||
ASSERT_NO_ERROR(acc.Commit());
|
||||
}
|
||||
{
|
||||
auto res = storage.CreateExistenceConstraint(label1, prop1);
|
||||
ASSERT_TRUE(res.HasError());
|
||||
EXPECT_EQ(res.GetError(),
|
||||
(ConstraintViolation{ConstraintViolation::Type::EXISTENCE, label1, std::set<PropertyId>{prop1}}));
|
||||
}
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
for (auto vertex : acc.Vertices(View::OLD)) {
|
||||
ASSERT_NO_ERROR(vertex.SetProperty(prop1, PropertyValue(1)));
|
||||
}
|
||||
ASSERT_NO_ERROR(acc.Commit());
|
||||
}
|
||||
{
|
||||
auto res = storage.CreateExistenceConstraint(label1, prop1);
|
||||
EXPECT_TRUE(res.HasValue() && res.GetValue());
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST_F(ConstraintsTest, ExistenceConstraintsViolationOnCommit) {
|
||||
{
|
||||
auto res = storage.CreateExistenceConstraint(label1, prop1);
|
||||
ASSERT_TRUE(res.HasValue() && res.GetValue());
|
||||
}
|
||||
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
auto vertex = acc.CreateVertex();
|
||||
ASSERT_NO_ERROR(vertex.AddLabel(label1));
|
||||
|
||||
auto res = acc.Commit();
|
||||
ASSERT_TRUE(res.HasError());
|
||||
EXPECT_EQ(res.GetError(),
|
||||
(ConstraintViolation{ConstraintViolation::Type::EXISTENCE, label1, std::set<PropertyId>{prop1}}));
|
||||
}
|
||||
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
auto vertex = acc.CreateVertex();
|
||||
ASSERT_NO_ERROR(vertex.AddLabel(label1));
|
||||
ASSERT_NO_ERROR(vertex.SetProperty(prop1, PropertyValue(1)));
|
||||
ASSERT_NO_ERROR(acc.Commit());
|
||||
}
|
||||
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
for (auto vertex : acc.Vertices(View::OLD)) {
|
||||
ASSERT_NO_ERROR(vertex.SetProperty(prop1, PropertyValue()));
|
||||
}
|
||||
|
||||
auto res = acc.Commit();
|
||||
ASSERT_TRUE(res.HasError());
|
||||
EXPECT_EQ(res.GetError(),
|
||||
(ConstraintViolation{ConstraintViolation::Type::EXISTENCE, label1, std::set<PropertyId>{prop1}}));
|
||||
}
|
||||
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
for (auto vertex : acc.Vertices(View::OLD)) {
|
||||
ASSERT_NO_ERROR(vertex.SetProperty(prop1, PropertyValue()));
|
||||
}
|
||||
for (auto vertex : acc.Vertices(View::OLD)) {
|
||||
ASSERT_NO_ERROR(acc.DeleteVertex(&vertex));
|
||||
}
|
||||
|
||||
ASSERT_NO_ERROR(acc.Commit());
|
||||
}
|
||||
|
||||
ASSERT_TRUE(storage.DropExistenceConstraint(label1, prop1));
|
||||
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
auto vertex = acc.CreateVertex();
|
||||
ASSERT_NO_ERROR(vertex.AddLabel(label1));
|
||||
ASSERT_NO_ERROR(acc.Commit());
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST_F(ConstraintsTest, UniqueConstraintsCreateAndDropAndList) {
|
||||
EXPECT_EQ(storage.ListAllConstraints().unique.size(), 0);
|
||||
{
|
||||
auto res = storage.CreateUniqueConstraint(label1, {prop1});
|
||||
EXPECT_TRUE(res.HasValue());
|
||||
EXPECT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
|
||||
}
|
||||
EXPECT_THAT(storage.ListAllConstraints().unique,
|
||||
UnorderedElementsAre(std::make_pair(label1, std::set<PropertyId>{prop1})));
|
||||
{
|
||||
auto res = storage.CreateUniqueConstraint(label1, {prop1});
|
||||
EXPECT_TRUE(res.HasValue());
|
||||
EXPECT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::ALREADY_EXISTS);
|
||||
}
|
||||
EXPECT_THAT(storage.ListAllConstraints().unique,
|
||||
UnorderedElementsAre(std::make_pair(label1, std::set<PropertyId>{prop1})));
|
||||
{
|
||||
auto res = storage.CreateUniqueConstraint(label2, {prop1});
|
||||
EXPECT_TRUE(res.HasValue() && res.GetValue() == UniqueConstraints::CreationStatus::SUCCESS);
|
||||
ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
|
||||
}
|
||||
EXPECT_THAT(storage.ListAllConstraints().unique,
|
||||
UnorderedElementsAre(std::make_pair(label1, std::set<PropertyId>{prop1}),
|
||||
std::make_pair(label2, std::set<PropertyId>{prop1})));
|
||||
EXPECT_EQ(storage.DropUniqueConstraint(label1, {prop1}), UniqueConstraints::DeletionStatus::SUCCESS);
|
||||
EXPECT_EQ(storage.DropUniqueConstraint(label1, {prop1}), UniqueConstraints::DeletionStatus::NOT_FOUND);
|
||||
EXPECT_THAT(storage.ListAllConstraints().unique,
|
||||
UnorderedElementsAre(std::make_pair(label2, std::set<PropertyId>{prop1})));
|
||||
EXPECT_EQ(storage.DropUniqueConstraint(label2, {prop1}), UniqueConstraints::DeletionStatus::SUCCESS);
|
||||
EXPECT_EQ(storage.DropUniqueConstraint(label2, {prop2}), UniqueConstraints::DeletionStatus::NOT_FOUND);
|
||||
EXPECT_EQ(storage.ListAllConstraints().unique.size(), 0);
|
||||
{
|
||||
auto res = storage.CreateUniqueConstraint(label2, {prop1});
|
||||
EXPECT_TRUE(res.HasValue());
|
||||
EXPECT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
|
||||
}
|
||||
EXPECT_THAT(storage.ListAllConstraints().unique,
|
||||
UnorderedElementsAre(std::make_pair(label2, std::set<PropertyId>{prop1})));
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST_F(ConstraintsTest, UniqueConstraintsCreateFailure1) {
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
auto vertex1 = acc.CreateVertex();
|
||||
ASSERT_NO_ERROR(vertex1.AddLabel(label1));
|
||||
ASSERT_NO_ERROR(vertex1.SetProperty(prop1, PropertyValue(1)));
|
||||
}
|
||||
ASSERT_NO_ERROR(acc.Commit());
|
||||
}
|
||||
|
||||
{
|
||||
auto res = storage.CreateUniqueConstraint(label1, {prop1});
|
||||
ASSERT_TRUE(res.HasError());
|
||||
EXPECT_EQ(res.GetError(),
|
||||
(ConstraintViolation{ConstraintViolation::Type::UNIQUE, label1, std::set<PropertyId>{prop1}}));
|
||||
}
|
||||
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
for (auto vertex : acc.Vertices(View::OLD)) {
|
||||
ASSERT_NO_ERROR(acc.DeleteVertex(&vertex));
|
||||
}
|
||||
ASSERT_NO_ERROR(acc.Commit());
|
||||
}
|
||||
|
||||
{
|
||||
auto res = storage.CreateUniqueConstraint(label1, {prop1});
|
||||
ASSERT_TRUE(res.HasValue());
|
||||
ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST_F(ConstraintsTest, UniqueConstraintsCreateFailure2) {
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
auto vertex = acc.CreateVertex();
|
||||
ASSERT_NO_ERROR(vertex.AddLabel(label1));
|
||||
ASSERT_NO_ERROR(vertex.SetProperty(prop1, PropertyValue(1)));
|
||||
}
|
||||
ASSERT_NO_ERROR(acc.Commit());
|
||||
}
|
||||
|
||||
{
|
||||
auto res = storage.CreateUniqueConstraint(label1, {prop1});
|
||||
ASSERT_TRUE(res.HasError());
|
||||
EXPECT_EQ(res.GetError(),
|
||||
(ConstraintViolation{ConstraintViolation::Type::UNIQUE, label1, std::set<PropertyId>{prop1}}));
|
||||
}
|
||||
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
int value = 0;
|
||||
for (auto vertex : acc.Vertices(View::OLD)) {
|
||||
ASSERT_NO_ERROR(vertex.SetProperty(prop1, PropertyValue(value)));
|
||||
++value;
|
||||
}
|
||||
ASSERT_NO_ERROR(acc.Commit());
|
||||
}
|
||||
|
||||
{
|
||||
auto res = storage.CreateUniqueConstraint(label1, {prop1});
|
||||
ASSERT_TRUE(res.HasValue());
|
||||
ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST_F(ConstraintsTest, UniqueConstraintsNoViolation1) {
|
||||
Gid gid1;
|
||||
Gid gid2;
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
auto vertex1 = acc.CreateVertex();
|
||||
auto vertex2 = acc.CreateVertex();
|
||||
gid1 = vertex1.Gid();
|
||||
gid2 = vertex2.Gid();
|
||||
|
||||
ASSERT_NO_ERROR(vertex1.AddLabel(label1));
|
||||
ASSERT_NO_ERROR(vertex1.SetProperty(prop1, PropertyValue(1)));
|
||||
ASSERT_NO_ERROR(acc.Commit());
|
||||
}
|
||||
|
||||
{
|
||||
auto res = storage.CreateUniqueConstraint(label1, {prop1, prop2});
|
||||
ASSERT_TRUE(res.HasValue());
|
||||
ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
|
||||
}
|
||||
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
auto vertex1 = acc.FindVertex(gid1, View::OLD);
|
||||
auto vertex2 = acc.FindVertex(gid2, View::OLD);
|
||||
|
||||
ASSERT_NO_ERROR(vertex1->SetProperty(prop2, PropertyValue(2)));
|
||||
ASSERT_NO_ERROR(vertex2->AddLabel(label1));
|
||||
ASSERT_NO_ERROR(vertex2->SetProperty(prop1, PropertyValue(1)));
|
||||
ASSERT_NO_ERROR(vertex2->SetProperty(prop2, PropertyValue(3)));
|
||||
ASSERT_NO_ERROR(acc.Commit());
|
||||
}
|
||||
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
auto vertex1 = acc.FindVertex(gid1, View::OLD);
|
||||
auto vertex2 = acc.FindVertex(gid2, View::OLD);
|
||||
ASSERT_NO_ERROR(vertex1->SetProperty(prop1, PropertyValue(2)));
|
||||
ASSERT_NO_ERROR(vertex2->SetProperty(prop1, PropertyValue(1)));
|
||||
ASSERT_NO_ERROR(acc.Commit());
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST_F(ConstraintsTest, UniqueConstraintsNoViolation2) {
|
||||
{
|
||||
auto res = storage.CreateUniqueConstraint(label1, {prop1});
|
||||
ASSERT_TRUE(res.HasValue());
|
||||
ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
|
||||
}
|
||||
|
||||
{
|
||||
// tx1: B---SP(v1, 1)---SP(v1, 2)---OK--
|
||||
// tx2: -B---SP(v2, 2)---SP(v2, 1)---OK-
|
||||
|
||||
auto acc1 = storage.Access();
|
||||
auto acc2 = storage.Access();
|
||||
auto vertex1 = acc1.CreateVertex();
|
||||
auto vertex2 = acc2.CreateVertex();
|
||||
|
||||
ASSERT_NO_ERROR(vertex1.AddLabel(label1));
|
||||
ASSERT_NO_ERROR(vertex1.SetProperty(prop1, PropertyValue(1)));
|
||||
ASSERT_NO_ERROR(vertex2.AddLabel(label1));
|
||||
ASSERT_NO_ERROR(vertex2.SetProperty(prop1, PropertyValue(2)));
|
||||
|
||||
ASSERT_NO_ERROR(vertex1.SetProperty(prop1, PropertyValue(2)));
|
||||
ASSERT_NO_ERROR(vertex2.SetProperty(prop1, PropertyValue(1)));
|
||||
|
||||
ASSERT_NO_ERROR(acc1.Commit());
|
||||
ASSERT_NO_ERROR(acc2.Commit());
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST_F(ConstraintsTest, UniqueConstraintsNoViolation3) {
|
||||
{
|
||||
auto res = storage.CreateUniqueConstraint(label1, {prop1});
|
||||
ASSERT_TRUE(res.HasValue());
|
||||
ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
|
||||
}
|
||||
|
||||
{
|
||||
// tx1: B---SP(v1, 1)---OK----------------------
|
||||
// tx2: --------------------B---SP(v1, 2)---OK--
|
||||
// tx3: ---------------------B---SP(v2, 1)---OK-
|
||||
|
||||
auto acc1 = storage.Access();
|
||||
auto vertex1 = acc1.CreateVertex();
|
||||
auto gid = vertex1.Gid();
|
||||
|
||||
ASSERT_NO_ERROR(vertex1.AddLabel(label1));
|
||||
ASSERT_NO_ERROR(vertex1.SetProperty(prop1, PropertyValue(1)));
|
||||
|
||||
ASSERT_NO_ERROR(acc1.Commit());
|
||||
|
||||
auto acc2 = storage.Access();
|
||||
auto acc3 = storage.Access();
|
||||
auto vertex2 = acc2.FindVertex(gid, View::NEW); // vertex1 == vertex2
|
||||
auto vertex3 = acc3.CreateVertex();
|
||||
|
||||
ASSERT_NO_ERROR(vertex2->SetProperty(prop1, PropertyValue(2)));
|
||||
ASSERT_NO_ERROR(vertex3.AddLabel(label1));
|
||||
ASSERT_NO_ERROR(vertex3.SetProperty(prop1, PropertyValue(1)));
|
||||
|
||||
ASSERT_NO_ERROR(acc2.Commit());
|
||||
ASSERT_NO_ERROR(acc3.Commit());
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST_F(ConstraintsTest, UniqueConstraintsNoViolation4) {
|
||||
{
|
||||
auto res = storage.CreateUniqueConstraint(label1, {prop1});
|
||||
ASSERT_TRUE(res.HasValue());
|
||||
ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
|
||||
}
|
||||
|
||||
{
|
||||
// tx1: B---SP(v1, 1)---OK-----------------------
|
||||
// tx2: --------------------B---SP(v2, 1)-----OK-
|
||||
// tx3: ---------------------B---SP(v1, 2)---OK--
|
||||
|
||||
auto acc1 = storage.Access();
|
||||
auto vertex1 = acc1.CreateVertex();
|
||||
auto gid = vertex1.Gid();
|
||||
|
||||
ASSERT_NO_ERROR(vertex1.AddLabel(label1));
|
||||
ASSERT_NO_ERROR(vertex1.SetProperty(prop1, PropertyValue(1)));
|
||||
|
||||
ASSERT_NO_ERROR(acc1.Commit());
|
||||
|
||||
auto acc2 = storage.Access();
|
||||
auto acc3 = storage.Access();
|
||||
auto vertex2 = acc2.CreateVertex();
|
||||
auto vertex3 = acc3.FindVertex(gid, View::NEW);
|
||||
|
||||
ASSERT_NO_ERROR(vertex2.AddLabel(label1));
|
||||
ASSERT_NO_ERROR(vertex2.SetProperty(prop1, PropertyValue(1)));
|
||||
ASSERT_NO_ERROR(vertex3->SetProperty(prop1, PropertyValue(2)));
|
||||
|
||||
ASSERT_NO_ERROR(acc3.Commit());
|
||||
ASSERT_NO_ERROR(acc2.Commit());
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST_F(ConstraintsTest, UniqueConstraintsViolationOnCommit1) {
|
||||
{
|
||||
auto res = storage.CreateUniqueConstraint(label1, {prop1});
|
||||
ASSERT_TRUE(res.HasValue());
|
||||
ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
|
||||
}
|
||||
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
auto vertex1 = acc.CreateVertex();
|
||||
auto vertex2 = acc.CreateVertex();
|
||||
ASSERT_NO_ERROR(vertex1.AddLabel(label1));
|
||||
ASSERT_NO_ERROR(vertex1.SetProperty(prop1, PropertyValue(1)));
|
||||
ASSERT_NO_ERROR(vertex2.AddLabel(label1));
|
||||
ASSERT_NO_ERROR(vertex2.SetProperty(prop1, PropertyValue(1)));
|
||||
auto res = acc.Commit();
|
||||
ASSERT_TRUE(res.HasError());
|
||||
EXPECT_EQ(res.GetError(),
|
||||
(ConstraintViolation{ConstraintViolation::Type::UNIQUE, label1, std::set<PropertyId>{prop1}}));
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST_F(ConstraintsTest, UniqueConstraintsViolationOnCommit2) {
|
||||
{
|
||||
auto res = storage.CreateUniqueConstraint(label1, {prop1});
|
||||
ASSERT_TRUE(res.HasValue());
|
||||
ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
|
||||
}
|
||||
|
||||
{
|
||||
// tx1: B---SP(v1, 1)---SP(v2, 2)---OK-----------------------
|
||||
// tx2: -------------------------------B---SP(v1, 3)---OK----
|
||||
// tx3: --------------------------------B---SP(v2, 3)---FAIL-
|
||||
|
||||
auto acc1 = storage.Access();
|
||||
auto vertex1 = acc1.CreateVertex();
|
||||
auto vertex2 = acc1.CreateVertex();
|
||||
auto gid1 = vertex1.Gid();
|
||||
auto gid2 = vertex2.Gid();
|
||||
|
||||
ASSERT_NO_ERROR(vertex1.AddLabel(label1));
|
||||
ASSERT_NO_ERROR(vertex1.SetProperty(prop1, PropertyValue(1)));
|
||||
ASSERT_NO_ERROR(vertex2.AddLabel(label1));
|
||||
ASSERT_NO_ERROR(vertex2.SetProperty(prop1, PropertyValue(2)));
|
||||
|
||||
ASSERT_NO_ERROR(acc1.Commit());
|
||||
|
||||
auto acc2 = storage.Access();
|
||||
auto acc3 = storage.Access();
|
||||
auto vertex3 = acc2.FindVertex(gid1, View::NEW); // vertex3 == vertex1
|
||||
auto vertex4 = acc3.FindVertex(gid2, View::NEW); // vertex4 == vertex2
|
||||
|
||||
ASSERT_NO_ERROR(vertex3->SetProperty(prop1, PropertyValue(3)));
|
||||
ASSERT_NO_ERROR(vertex4->SetProperty(prop1, PropertyValue(3)));
|
||||
|
||||
ASSERT_NO_ERROR(acc2.Commit());
|
||||
auto res = acc3.Commit();
|
||||
ASSERT_TRUE(res.HasError());
|
||||
EXPECT_EQ(res.GetError(),
|
||||
(ConstraintViolation{ConstraintViolation::Type::UNIQUE, label1, std::set<PropertyId>{prop1}}));
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST_F(ConstraintsTest, UniqueConstraintsViolationOnCommit3) {
|
||||
{
|
||||
auto res = storage.CreateUniqueConstraint(label1, {prop1});
|
||||
ASSERT_TRUE(res.HasValue());
|
||||
ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
|
||||
}
|
||||
|
||||
{
|
||||
// tx1: B---SP(v1, 1)---SP(v2, 2)---OK-----------------------
|
||||
// tx2: -------------------------------B---SP(v1, 2)---FAIL--
|
||||
// tx3: --------------------------------B---SP(v2, 1)---FAIL-
|
||||
|
||||
auto acc1 = storage.Access();
|
||||
auto vertex1 = acc1.CreateVertex();
|
||||
auto vertex2 = acc1.CreateVertex();
|
||||
auto gid1 = vertex1.Gid();
|
||||
auto gid2 = vertex2.Gid();
|
||||
|
||||
ASSERT_NO_ERROR(vertex1.AddLabel(label1));
|
||||
ASSERT_NO_ERROR(vertex1.SetProperty(prop1, PropertyValue(1)));
|
||||
ASSERT_NO_ERROR(vertex2.AddLabel(label1));
|
||||
ASSERT_NO_ERROR(vertex2.SetProperty(prop1, PropertyValue(2)));
|
||||
|
||||
ASSERT_NO_ERROR(acc1.Commit());
|
||||
|
||||
auto acc2 = storage.Access();
|
||||
auto acc3 = storage.Access();
|
||||
auto vertex3 = acc2.FindVertex(gid1, View::OLD); // vertex3 == vertex1
|
||||
auto vertex4 = acc3.FindVertex(gid2, View::OLD); // vertex4 == vertex2
|
||||
|
||||
// Setting `prop2` shouldn't affect the remaining code.
|
||||
ASSERT_NO_ERROR(vertex3->SetProperty(prop2, PropertyValue(3)));
|
||||
ASSERT_NO_ERROR(vertex4->SetProperty(prop2, PropertyValue(3)));
|
||||
|
||||
ASSERT_NO_ERROR(vertex3->SetProperty(prop1, PropertyValue(2)));
|
||||
ASSERT_NO_ERROR(vertex4->SetProperty(prop1, PropertyValue(1)));
|
||||
|
||||
auto res = acc2.Commit();
|
||||
ASSERT_TRUE(res.HasError());
|
||||
EXPECT_EQ(res.GetError(),
|
||||
(ConstraintViolation{ConstraintViolation::Type::UNIQUE, label1, std::set<PropertyId>{prop1}}));
|
||||
res = acc3.Commit();
|
||||
ASSERT_TRUE(res.HasError());
|
||||
EXPECT_EQ(res.GetError(),
|
||||
(ConstraintViolation{ConstraintViolation::Type::UNIQUE, label1, std::set<PropertyId>{prop1}}));
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST_F(ConstraintsTest, UniqueConstraintsLabelAlteration) {
|
||||
{
|
||||
auto res = storage.CreateUniqueConstraint(label1, {prop1});
|
||||
ASSERT_TRUE(res.HasValue());
|
||||
ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
|
||||
}
|
||||
|
||||
Gid gid1;
|
||||
Gid gid2;
|
||||
{
|
||||
// B---AL(v2)---SP(v1, 1)---SP(v2, 1)---OK
|
||||
|
||||
auto acc = storage.Access();
|
||||
auto vertex1 = acc.CreateVertex();
|
||||
auto vertex2 = acc.CreateVertex();
|
||||
gid1 = vertex1.Gid();
|
||||
gid2 = vertex2.Gid();
|
||||
|
||||
ASSERT_NO_ERROR(vertex1.AddLabel(label2));
|
||||
ASSERT_NO_ERROR(vertex1.SetProperty(prop1, PropertyValue(1)));
|
||||
ASSERT_NO_ERROR(vertex2.AddLabel(label1));
|
||||
ASSERT_NO_ERROR(vertex2.SetProperty(prop1, PropertyValue(1)));
|
||||
|
||||
ASSERT_NO_ERROR(acc.Commit());
|
||||
}
|
||||
|
||||
{
|
||||
// tx1: B---AL(v1)-----OK-
|
||||
// tx2: -B---RL(v2)---OK--
|
||||
|
||||
auto acc1 = storage.Access();
|
||||
auto acc2 = storage.Access();
|
||||
auto vertex1 = acc1.FindVertex(gid1, View::OLD);
|
||||
auto vertex2 = acc2.FindVertex(gid2, View::OLD);
|
||||
|
||||
ASSERT_NO_ERROR(vertex1->AddLabel(label1));
|
||||
ASSERT_NO_ERROR(vertex2->RemoveLabel(label1));
|
||||
|
||||
// Reapplying labels shouldn't affect the remaining code.
|
||||
ASSERT_NO_ERROR(vertex1->RemoveLabel(label1));
|
||||
ASSERT_NO_ERROR(vertex2->AddLabel(label1));
|
||||
ASSERT_NO_ERROR(vertex1->AddLabel(label1));
|
||||
ASSERT_NO_ERROR(vertex2->RemoveLabel(label1));
|
||||
ASSERT_NO_ERROR(vertex1->RemoveLabel(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(label1));
|
||||
ASSERT_NO_ERROR(vertex1->AddLabel(label1));
|
||||
|
||||
// Commit the first transaction.
|
||||
ASSERT_NO_ERROR(acc1.Commit());
|
||||
}
|
||||
|
||||
{
|
||||
// B---AL(v2)---FAIL
|
||||
|
||||
auto acc = storage.Access();
|
||||
auto vertex2 = acc.FindVertex(gid2, View::OLD);
|
||||
ASSERT_NO_ERROR(vertex2->AddLabel(label1));
|
||||
|
||||
auto res = acc.Commit();
|
||||
ASSERT_TRUE(res.HasError());
|
||||
EXPECT_EQ(res.GetError(), (ConstraintViolation{ConstraintViolation::Type::UNIQUE, label1, std::set{prop1}}));
|
||||
}
|
||||
|
||||
{
|
||||
// B---RL(v1)---OK
|
||||
|
||||
auto acc = storage.Access();
|
||||
auto vertex1 = acc.FindVertex(gid1, View::OLD);
|
||||
ASSERT_NO_ERROR(vertex1->RemoveLabel(label1));
|
||||
ASSERT_NO_ERROR(acc.Commit());
|
||||
}
|
||||
|
||||
{
|
||||
// tx1: B---AL(v1)-----FAIL
|
||||
// tx2: -B---AL(v2)---OK---
|
||||
|
||||
auto acc1 = storage.Access();
|
||||
auto acc2 = storage.Access();
|
||||
auto vertex1 = acc1.FindVertex(gid1, View::OLD);
|
||||
auto vertex2 = acc2.FindVertex(gid2, View::OLD);
|
||||
|
||||
ASSERT_NO_ERROR(vertex1->AddLabel(label1));
|
||||
ASSERT_NO_ERROR(vertex2->AddLabel(label1));
|
||||
|
||||
// Reapply everything.
|
||||
ASSERT_NO_ERROR(vertex1->RemoveLabel(label1));
|
||||
ASSERT_NO_ERROR(vertex2->RemoveLabel(label1));
|
||||
ASSERT_NO_ERROR(vertex1->AddLabel(label1));
|
||||
ASSERT_NO_ERROR(vertex2->AddLabel(label1));
|
||||
|
||||
ASSERT_NO_ERROR(acc2.Commit());
|
||||
|
||||
auto res = acc1.Commit();
|
||||
ASSERT_TRUE(res.HasError());
|
||||
EXPECT_EQ(res.GetError(), (ConstraintViolation{ConstraintViolation::Type::UNIQUE, label1, std::set{prop1}}));
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST_F(ConstraintsTest, UniqueConstraintsPropertySetSize) {
|
||||
{
|
||||
// This should fail since unique constraint cannot be created for an empty
|
||||
// property set.
|
||||
auto res = storage.CreateUniqueConstraint(label1, {});
|
||||
ASSERT_TRUE(res.HasValue());
|
||||
ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::EMPTY_PROPERTIES);
|
||||
}
|
||||
|
||||
// Removing a constraint with empty property set should also fail.
|
||||
ASSERT_EQ(storage.DropUniqueConstraint(label1, {}), UniqueConstraints::DeletionStatus::EMPTY_PROPERTIES);
|
||||
|
||||
// Create a set of 33 properties.
|
||||
std::set<PropertyId> properties;
|
||||
for (int i = 1; i <= 33; ++i) {
|
||||
properties.insert(storage.NameToProperty("prop" + std::to_string(i)));
|
||||
}
|
||||
|
||||
{
|
||||
// This should fail since list of properties exceeds the maximum number of
|
||||
// properties, which is 32.
|
||||
auto res = storage.CreateUniqueConstraint(label1, properties);
|
||||
ASSERT_TRUE(res.HasValue());
|
||||
ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::PROPERTIES_SIZE_LIMIT_EXCEEDED);
|
||||
}
|
||||
|
||||
// An attempt to delete constraint with too large property set should fail.
|
||||
ASSERT_EQ(storage.DropUniqueConstraint(label1, properties),
|
||||
UniqueConstraints::DeletionStatus::PROPERTIES_SIZE_LIMIT_EXCEEDED);
|
||||
|
||||
// Remove one property from the set.
|
||||
properties.erase(properties.begin());
|
||||
|
||||
{
|
||||
// Creating a constraint for 32 properties should succeed.
|
||||
auto res = storage.CreateUniqueConstraint(label1, properties);
|
||||
ASSERT_TRUE(res.HasValue());
|
||||
ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
|
||||
}
|
||||
|
||||
EXPECT_THAT(storage.ListAllConstraints().unique, UnorderedElementsAre(std::make_pair(label1, properties)));
|
||||
|
||||
// Removing a constraint with 32 properties should succeed.
|
||||
ASSERT_EQ(storage.DropUniqueConstraint(label1, properties), UniqueConstraints::DeletionStatus::SUCCESS);
|
||||
ASSERT_TRUE(storage.ListAllConstraints().unique.empty());
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST_F(ConstraintsTest, UniqueConstraintsMultipleProperties) {
|
||||
{
|
||||
auto res = storage.CreateUniqueConstraint(label1, {prop1, prop2});
|
||||
ASSERT_TRUE(res.HasValue());
|
||||
ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
|
||||
}
|
||||
|
||||
{
|
||||
// An attempt to create an existing unique constraint.
|
||||
auto res = storage.CreateUniqueConstraint(label1, {prop2, prop1});
|
||||
ASSERT_TRUE(res.HasValue());
|
||||
ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::ALREADY_EXISTS);
|
||||
}
|
||||
|
||||
Gid gid1;
|
||||
Gid gid2;
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
auto vertex1 = acc.CreateVertex();
|
||||
auto vertex2 = acc.CreateVertex();
|
||||
gid1 = vertex1.Gid();
|
||||
gid2 = vertex2.Gid();
|
||||
|
||||
ASSERT_NO_ERROR(vertex1.AddLabel(label1));
|
||||
ASSERT_NO_ERROR(vertex1.SetProperty(prop1, PropertyValue(1)));
|
||||
ASSERT_NO_ERROR(vertex1.SetProperty(prop2, PropertyValue(2)));
|
||||
|
||||
ASSERT_NO_ERROR(vertex2.AddLabel(label1));
|
||||
ASSERT_NO_ERROR(vertex2.SetProperty(prop1, PropertyValue(1)));
|
||||
ASSERT_NO_ERROR(vertex2.SetProperty(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 = storage.Access();
|
||||
auto vertex2 = acc.FindVertex(gid2, View::OLD);
|
||||
ASSERT_NO_ERROR(vertex2->SetProperty(prop2, PropertyValue(2)));
|
||||
auto res = acc.Commit();
|
||||
ASSERT_TRUE(res.HasError());
|
||||
EXPECT_EQ(res.GetError(),
|
||||
(ConstraintViolation{ConstraintViolation::Type::UNIQUE, label1, std::set<PropertyId>{prop1, 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 = storage.Access();
|
||||
auto vertex1 = acc.FindVertex(gid1, View::OLD);
|
||||
auto vertex2 = acc.FindVertex(gid2, View::OLD);
|
||||
ASSERT_NO_ERROR(vertex1->SetProperty(prop2, PropertyValue()));
|
||||
ASSERT_NO_ERROR(vertex2->SetProperty(prop2, PropertyValue()));
|
||||
ASSERT_NO_ERROR(acc.Commit());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ConstraintsTest, UniqueConstraintsInsertAbortInsert) {
|
||||
{
|
||||
auto res = storage.CreateUniqueConstraint(label1, {prop1, prop2});
|
||||
ASSERT_TRUE(res.HasValue());
|
||||
ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
|
||||
}
|
||||
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
auto vertex = acc.CreateVertex();
|
||||
ASSERT_NO_ERROR(vertex.AddLabel(label1));
|
||||
ASSERT_NO_ERROR(vertex.SetProperty(prop1, PropertyValue(1)));
|
||||
ASSERT_NO_ERROR(vertex.SetProperty(prop2, PropertyValue(2)));
|
||||
acc.Abort();
|
||||
}
|
||||
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
auto vertex = acc.CreateVertex();
|
||||
ASSERT_NO_ERROR(vertex.AddLabel(label1));
|
||||
ASSERT_NO_ERROR(vertex.SetProperty(prop2, PropertyValue(2)));
|
||||
ASSERT_NO_ERROR(vertex.SetProperty(prop1, PropertyValue(1)));
|
||||
ASSERT_NO_ERROR(acc.Commit());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ConstraintsTest, UniqueConstraintsInsertRemoveInsert) {
|
||||
{
|
||||
auto res = storage.CreateUniqueConstraint(label1, {prop1, prop2});
|
||||
ASSERT_TRUE(res.HasValue());
|
||||
ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
|
||||
}
|
||||
|
||||
Gid gid;
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
auto vertex = acc.CreateVertex();
|
||||
gid = vertex.Gid();
|
||||
ASSERT_NO_ERROR(vertex.AddLabel(label1));
|
||||
ASSERT_NO_ERROR(vertex.SetProperty(prop1, PropertyValue(1)));
|
||||
ASSERT_NO_ERROR(vertex.SetProperty(prop2, PropertyValue(2)));
|
||||
ASSERT_NO_ERROR(acc.Commit());
|
||||
}
|
||||
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
auto vertex = acc.FindVertex(gid, View::OLD);
|
||||
ASSERT_NO_ERROR(acc.DeleteVertex(&*vertex));
|
||||
ASSERT_NO_ERROR(acc.Commit());
|
||||
}
|
||||
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
auto vertex = acc.CreateVertex();
|
||||
ASSERT_NO_ERROR(vertex.AddLabel(label1));
|
||||
ASSERT_NO_ERROR(vertex.SetProperty(prop1, PropertyValue(1)));
|
||||
ASSERT_NO_ERROR(vertex.SetProperty(prop2, PropertyValue(2)));
|
||||
ASSERT_NO_ERROR(acc.Commit());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ConstraintsTest, UniqueConstraintsInsertRemoveAbortInsert) {
|
||||
{
|
||||
auto res = storage.CreateUniqueConstraint(label1, {prop1, prop2});
|
||||
ASSERT_TRUE(res.HasValue());
|
||||
ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
|
||||
}
|
||||
|
||||
Gid gid;
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
auto vertex = acc.CreateVertex();
|
||||
gid = vertex.Gid();
|
||||
ASSERT_NO_ERROR(vertex.AddLabel(label1));
|
||||
ASSERT_NO_ERROR(vertex.SetProperty(prop1, PropertyValue(2)));
|
||||
ASSERT_NO_ERROR(vertex.SetProperty(prop2, PropertyValue(1)));
|
||||
ASSERT_NO_ERROR(acc.Commit());
|
||||
}
|
||||
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
auto vertex = acc.FindVertex(gid, View::OLD);
|
||||
ASSERT_NO_ERROR(acc.DeleteVertex(&*vertex));
|
||||
acc.Abort();
|
||||
}
|
||||
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
auto vertex = acc.CreateVertex();
|
||||
ASSERT_NO_ERROR(vertex.AddLabel(label1));
|
||||
ASSERT_NO_ERROR(vertex.SetProperty(prop2, PropertyValue(1)));
|
||||
ASSERT_NO_ERROR(vertex.SetProperty(prop1, PropertyValue(2)));
|
||||
|
||||
auto res = acc.Commit();
|
||||
ASSERT_TRUE(res.HasError());
|
||||
EXPECT_EQ(res.GetError(), (ConstraintViolation{ConstraintViolation::Type::UNIQUE, label1, std::set{prop1, prop2}}));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ConstraintsTest, UniqueConstraintsDeleteVertexSetProperty) {
|
||||
{
|
||||
auto res = storage.CreateUniqueConstraint(label1, {prop1});
|
||||
ASSERT_TRUE(res.HasValue());
|
||||
ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
|
||||
}
|
||||
|
||||
Gid gid1;
|
||||
Gid gid2;
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
auto vertex1 = acc.CreateVertex();
|
||||
auto vertex2 = acc.CreateVertex();
|
||||
gid1 = vertex1.Gid();
|
||||
gid2 = vertex2.Gid();
|
||||
|
||||
ASSERT_NO_ERROR(vertex1.AddLabel(label1));
|
||||
ASSERT_NO_ERROR(vertex2.AddLabel(label1));
|
||||
ASSERT_NO_ERROR(vertex1.SetProperty(prop1, PropertyValue(1)));
|
||||
ASSERT_NO_ERROR(vertex2.SetProperty(prop1, PropertyValue(2)));
|
||||
|
||||
ASSERT_NO_ERROR(acc.Commit());
|
||||
}
|
||||
|
||||
{
|
||||
auto acc1 = storage.Access();
|
||||
auto acc2 = storage.Access();
|
||||
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(prop1, PropertyValue(2)));
|
||||
|
||||
auto res = acc1.Commit();
|
||||
ASSERT_TRUE(res.HasError());
|
||||
EXPECT_EQ(res.GetError(), (ConstraintViolation{ConstraintViolation::Type::UNIQUE, label1, std::set{prop1}}));
|
||||
|
||||
ASSERT_NO_ERROR(acc2.Commit());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ConstraintsTest, UniqueConstraintsInsertDropInsert) {
|
||||
{
|
||||
auto res = storage.CreateUniqueConstraint(label1, {prop1, prop2});
|
||||
ASSERT_TRUE(res.HasValue());
|
||||
ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
|
||||
}
|
||||
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
auto vertex = acc.CreateVertex();
|
||||
ASSERT_NO_ERROR(vertex.AddLabel(label1));
|
||||
ASSERT_NO_ERROR(vertex.SetProperty(prop1, PropertyValue(1)));
|
||||
ASSERT_NO_ERROR(vertex.SetProperty(prop2, PropertyValue(2)));
|
||||
ASSERT_NO_ERROR(acc.Commit());
|
||||
}
|
||||
|
||||
ASSERT_EQ(storage.DropUniqueConstraint(label1, {prop2, prop1}), UniqueConstraints::DeletionStatus::SUCCESS);
|
||||
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
auto vertex = acc.CreateVertex();
|
||||
ASSERT_NO_ERROR(vertex.AddLabel(label1));
|
||||
ASSERT_NO_ERROR(vertex.SetProperty(prop2, PropertyValue(2)));
|
||||
ASSERT_NO_ERROR(vertex.SetProperty(prop1, PropertyValue(1)));
|
||||
ASSERT_NO_ERROR(acc.Commit());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ConstraintsTest, UniqueConstraintsComparePropertyValues) {
|
||||
// Purpose of this test is to make sure that extracted property values
|
||||
// are correctly compared.
|
||||
|
||||
{
|
||||
auto res = storage.CreateUniqueConstraint(label1, {prop1, prop2});
|
||||
ASSERT_TRUE(res.HasValue());
|
||||
ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
|
||||
}
|
||||
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
auto vertex = acc.CreateVertex();
|
||||
ASSERT_NO_ERROR(vertex.AddLabel(label1));
|
||||
ASSERT_NO_ERROR(vertex.SetProperty(prop1, PropertyValue(2)));
|
||||
ASSERT_NO_ERROR(vertex.SetProperty(prop2, PropertyValue(1)));
|
||||
ASSERT_NO_ERROR(acc.Commit());
|
||||
}
|
||||
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
auto vertex = acc.CreateVertex();
|
||||
ASSERT_NO_ERROR(vertex.AddLabel(label1));
|
||||
ASSERT_NO_ERROR(vertex.SetProperty(prop1, PropertyValue(1)));
|
||||
ASSERT_NO_ERROR(vertex.SetProperty(prop2, PropertyValue(2)));
|
||||
ASSERT_NO_ERROR(acc.Commit());
|
||||
}
|
||||
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
auto vertex = acc.CreateVertex();
|
||||
ASSERT_NO_ERROR(vertex.AddLabel(label1));
|
||||
ASSERT_NO_ERROR(vertex.SetProperty(prop2, PropertyValue(0)));
|
||||
ASSERT_NO_ERROR(vertex.SetProperty(prop1, PropertyValue(3)));
|
||||
ASSERT_NO_ERROR(acc.Commit());
|
||||
}
|
||||
}
|
@ -1,461 +0,0 @@
|
||||
// Copyright 2022 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 <gtest/gtest.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <limits>
|
||||
|
||||
#include "storage/v2/durability/serialization.hpp"
|
||||
#include "storage/v2/property_value.hpp"
|
||||
#include "storage/v2/temporal.hpp"
|
||||
|
||||
static const std::string kTestMagic{"MGtest"};
|
||||
static const uint64_t kTestVersion{1};
|
||||
|
||||
class DecoderEncoderTest : public ::testing::Test {
|
||||
public:
|
||||
void SetUp() override { Clear(); }
|
||||
|
||||
void TearDown() override { Clear(); }
|
||||
|
||||
std::filesystem::path storage_file{std::filesystem::temp_directory_path() /
|
||||
"MG_test_unit_storage_v2_decoder_encoder.bin"};
|
||||
|
||||
std::filesystem::path alternate_file{std::filesystem::temp_directory_path() /
|
||||
"MG_test_unit_storage_v2_decoder_encoder_alternate.bin"};
|
||||
|
||||
private:
|
||||
void Clear() {
|
||||
if (std::filesystem::exists(storage_file)) {
|
||||
std::filesystem::remove(storage_file);
|
||||
}
|
||||
if (std::filesystem::exists(alternate_file)) {
|
||||
std::filesystem::remove(alternate_file);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST_F(DecoderEncoderTest, ReadMarker) {
|
||||
{
|
||||
memgraph::storage::durability::Encoder encoder;
|
||||
encoder.Initialize(storage_file, kTestMagic, kTestVersion);
|
||||
for (const auto &item : memgraph::storage::durability::kMarkersAll) {
|
||||
encoder.WriteMarker(item);
|
||||
}
|
||||
{
|
||||
uint8_t invalid = 1;
|
||||
encoder.Write(&invalid, sizeof(invalid));
|
||||
}
|
||||
encoder.Finalize();
|
||||
}
|
||||
{
|
||||
memgraph::storage::durability::Decoder decoder;
|
||||
auto version = decoder.Initialize(storage_file, kTestMagic);
|
||||
ASSERT_TRUE(version);
|
||||
ASSERT_EQ(*version, kTestVersion);
|
||||
for (const auto &item : memgraph::storage::durability::kMarkersAll) {
|
||||
auto decoded = decoder.ReadMarker();
|
||||
ASSERT_TRUE(decoded);
|
||||
ASSERT_EQ(*decoded, item);
|
||||
}
|
||||
ASSERT_FALSE(decoder.ReadMarker());
|
||||
ASSERT_FALSE(decoder.ReadMarker());
|
||||
auto pos = decoder.GetPosition();
|
||||
ASSERT_TRUE(pos);
|
||||
ASSERT_EQ(pos, decoder.GetSize());
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
|
||||
#define GENERATE_READ_TEST(name, type, ...) \
|
||||
TEST_F(DecoderEncoderTest, Read##name) { \
|
||||
std::vector<type> dataset{__VA_ARGS__}; \
|
||||
{ \
|
||||
memgraph::storage::durability::Encoder encoder; \
|
||||
encoder.Initialize(storage_file, kTestMagic, kTestVersion); \
|
||||
for (const auto &item : dataset) { \
|
||||
encoder.Write##name(item); \
|
||||
} \
|
||||
{ \
|
||||
uint8_t invalid = 1; \
|
||||
encoder.Write(&invalid, sizeof(invalid)); \
|
||||
} \
|
||||
encoder.Finalize(); \
|
||||
} \
|
||||
{ \
|
||||
memgraph::storage::durability::Decoder decoder; \
|
||||
auto version = decoder.Initialize(storage_file, kTestMagic); \
|
||||
ASSERT_TRUE(version); \
|
||||
ASSERT_EQ(*version, kTestVersion); \
|
||||
for (const auto &item : dataset) { \
|
||||
auto decoded = decoder.Read##name(); \
|
||||
ASSERT_TRUE(decoded); \
|
||||
ASSERT_EQ(*decoded, item); \
|
||||
} \
|
||||
ASSERT_FALSE(decoder.Read##name()); \
|
||||
ASSERT_FALSE(decoder.Read##name()); \
|
||||
auto pos = decoder.GetPosition(); \
|
||||
ASSERT_TRUE(pos); \
|
||||
ASSERT_EQ(pos, decoder.GetSize()); \
|
||||
} \
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
GENERATE_READ_TEST(Bool, bool, false, true);
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
GENERATE_READ_TEST(Uint, uint64_t, 0, 1, 1000, 123123123, std::numeric_limits<uint64_t>::max());
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
GENERATE_READ_TEST(Double, double, 1.123, 3.1415926535, 0, -505.505, std::numeric_limits<double>::infinity(),
|
||||
-std::numeric_limits<double>::infinity());
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
GENERATE_READ_TEST(String, std::string, "hello", "world", "nandare", "haihaihai", std::string(),
|
||||
std::string(100000, 'a'));
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
GENERATE_READ_TEST(
|
||||
PropertyValue, memgraph::storage::PropertyValue, memgraph::storage::PropertyValue(),
|
||||
memgraph::storage::PropertyValue(false), memgraph::storage::PropertyValue(true),
|
||||
memgraph::storage::PropertyValue(123L), memgraph::storage::PropertyValue(123.5),
|
||||
memgraph::storage::PropertyValue("nandare"),
|
||||
memgraph::storage::PropertyValue(std::vector<memgraph::storage::PropertyValue>{
|
||||
memgraph::storage::PropertyValue("nandare"), memgraph::storage::PropertyValue(123L)}),
|
||||
memgraph::storage::PropertyValue(std::map<std::string, memgraph::storage::PropertyValue>{
|
||||
{"nandare", memgraph::storage::PropertyValue(123)}}),
|
||||
memgraph::storage::PropertyValue(memgraph::storage::TemporalData(memgraph::storage::TemporalType::Date, 23)));
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
|
||||
#define GENERATE_SKIP_TEST(name, type, ...) \
|
||||
TEST_F(DecoderEncoderTest, Skip##name) { \
|
||||
std::vector<type> dataset{__VA_ARGS__}; \
|
||||
{ \
|
||||
memgraph::storage::durability::Encoder encoder; \
|
||||
encoder.Initialize(storage_file, kTestMagic, kTestVersion); \
|
||||
for (const auto &item : dataset) { \
|
||||
encoder.Write##name(item); \
|
||||
} \
|
||||
{ \
|
||||
uint8_t invalid = 1; \
|
||||
encoder.Write(&invalid, sizeof(invalid)); \
|
||||
} \
|
||||
encoder.Finalize(); \
|
||||
} \
|
||||
{ \
|
||||
memgraph::storage::durability::Decoder decoder; \
|
||||
auto version = decoder.Initialize(storage_file, kTestMagic); \
|
||||
ASSERT_TRUE(version); \
|
||||
ASSERT_EQ(*version, kTestVersion); \
|
||||
for (auto it = dataset.begin(); it != dataset.end(); ++it) { \
|
||||
ASSERT_TRUE(decoder.Skip##name()); \
|
||||
} \
|
||||
ASSERT_FALSE(decoder.Skip##name()); \
|
||||
ASSERT_FALSE(decoder.Skip##name()); \
|
||||
auto pos = decoder.GetPosition(); \
|
||||
ASSERT_TRUE(pos); \
|
||||
ASSERT_EQ(pos, decoder.GetSize()); \
|
||||
} \
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
GENERATE_SKIP_TEST(String, std::string, "hello", "world", "nandare", "haihaihai", std::string(500000, 'a'));
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
GENERATE_SKIP_TEST(
|
||||
PropertyValue, memgraph::storage::PropertyValue, memgraph::storage::PropertyValue(),
|
||||
memgraph::storage::PropertyValue(false), memgraph::storage::PropertyValue(true),
|
||||
memgraph::storage::PropertyValue(123L), memgraph::storage::PropertyValue(123.5),
|
||||
memgraph::storage::PropertyValue("nandare"),
|
||||
memgraph::storage::PropertyValue(std::vector<memgraph::storage::PropertyValue>{
|
||||
memgraph::storage::PropertyValue("nandare"), memgraph::storage::PropertyValue(123L)}),
|
||||
memgraph::storage::PropertyValue(std::map<std::string, memgraph::storage::PropertyValue>{
|
||||
{"nandare", memgraph::storage::PropertyValue(123)}}),
|
||||
memgraph::storage::PropertyValue(memgraph::storage::TemporalData(memgraph::storage::TemporalType::Date, 23)));
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
|
||||
#define GENERATE_PARTIAL_READ_TEST(name, value) \
|
||||
TEST_F(DecoderEncoderTest, PartialRead##name) { \
|
||||
{ \
|
||||
memgraph::storage::durability::Encoder encoder; \
|
||||
encoder.Initialize(storage_file, kTestMagic, kTestVersion); \
|
||||
encoder.Write##name(value); \
|
||||
encoder.Finalize(); \
|
||||
} \
|
||||
{ \
|
||||
memgraph::utils::InputFile ifile; \
|
||||
memgraph::utils::OutputFile ofile; \
|
||||
ASSERT_TRUE(ifile.Open(storage_file)); \
|
||||
ofile.Open(alternate_file, memgraph::utils::OutputFile::Mode::OVERWRITE_EXISTING); \
|
||||
auto size = ifile.GetSize(); \
|
||||
for (size_t i = 0; i <= size; ++i) { \
|
||||
if (i != 0) { \
|
||||
uint8_t byte; \
|
||||
ASSERT_TRUE(ifile.Read(&byte, sizeof(byte))); \
|
||||
ofile.Write(&byte, sizeof(byte)); \
|
||||
ofile.Sync(); \
|
||||
} \
|
||||
memgraph::storage::durability::Decoder decoder; \
|
||||
auto version = decoder.Initialize(alternate_file, kTestMagic); \
|
||||
if (i < kTestMagic.size() + sizeof(kTestVersion)) { \
|
||||
ASSERT_FALSE(version); \
|
||||
} else { \
|
||||
ASSERT_TRUE(version); \
|
||||
ASSERT_EQ(*version, kTestVersion); \
|
||||
} \
|
||||
if (i != size) { \
|
||||
ASSERT_FALSE(decoder.Read##name()); \
|
||||
} else { \
|
||||
auto decoded = decoder.Read##name(); \
|
||||
ASSERT_TRUE(decoded); \
|
||||
ASSERT_EQ(*decoded, value); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
GENERATE_PARTIAL_READ_TEST(Marker, memgraph::storage::durability::Marker::SECTION_VERTEX);
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
GENERATE_PARTIAL_READ_TEST(Bool, false);
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
GENERATE_PARTIAL_READ_TEST(Uint, 123123123);
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
GENERATE_PARTIAL_READ_TEST(Double, 3.1415926535);
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
GENERATE_PARTIAL_READ_TEST(String, "nandare");
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
GENERATE_PARTIAL_READ_TEST(
|
||||
PropertyValue,
|
||||
memgraph::storage::PropertyValue(std::vector<memgraph::storage::PropertyValue>{
|
||||
memgraph::storage::PropertyValue(), memgraph::storage::PropertyValue(true),
|
||||
memgraph::storage::PropertyValue(123L), memgraph::storage::PropertyValue(123.5),
|
||||
memgraph::storage::PropertyValue("nandare"),
|
||||
memgraph::storage::PropertyValue{
|
||||
std::map<std::string, memgraph::storage::PropertyValue>{{"haihai", memgraph::storage::PropertyValue()}}},
|
||||
memgraph::storage::PropertyValue(memgraph::storage::TemporalData(memgraph::storage::TemporalType::Date, 23))}));
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
|
||||
#define GENERATE_PARTIAL_SKIP_TEST(name, value) \
|
||||
TEST_F(DecoderEncoderTest, PartialSkip##name) { \
|
||||
{ \
|
||||
memgraph::storage::durability::Encoder encoder; \
|
||||
encoder.Initialize(storage_file, kTestMagic, kTestVersion); \
|
||||
encoder.Write##name(value); \
|
||||
encoder.Finalize(); \
|
||||
} \
|
||||
{ \
|
||||
memgraph::utils::InputFile ifile; \
|
||||
memgraph::utils::OutputFile ofile; \
|
||||
ASSERT_TRUE(ifile.Open(storage_file)); \
|
||||
ofile.Open(alternate_file, memgraph::utils::OutputFile::Mode::OVERWRITE_EXISTING); \
|
||||
auto size = ifile.GetSize(); \
|
||||
for (size_t i = 0; i <= size; ++i) { \
|
||||
if (i != 0) { \
|
||||
uint8_t byte; \
|
||||
ASSERT_TRUE(ifile.Read(&byte, sizeof(byte))); \
|
||||
ofile.Write(&byte, sizeof(byte)); \
|
||||
ofile.Sync(); \
|
||||
} \
|
||||
memgraph::storage::durability::Decoder decoder; \
|
||||
auto version = decoder.Initialize(alternate_file, kTestMagic); \
|
||||
if (i < kTestMagic.size() + sizeof(kTestVersion)) { \
|
||||
ASSERT_FALSE(version); \
|
||||
} else { \
|
||||
ASSERT_TRUE(version); \
|
||||
ASSERT_EQ(*version, kTestVersion); \
|
||||
} \
|
||||
if (i != size) { \
|
||||
ASSERT_FALSE(decoder.Skip##name()); \
|
||||
} else { \
|
||||
ASSERT_TRUE(decoder.Skip##name()); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
GENERATE_PARTIAL_SKIP_TEST(String, "nandare");
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
GENERATE_PARTIAL_SKIP_TEST(
|
||||
PropertyValue,
|
||||
memgraph::storage::PropertyValue(std::vector<memgraph::storage::PropertyValue>{
|
||||
memgraph::storage::PropertyValue(), memgraph::storage::PropertyValue(true),
|
||||
memgraph::storage::PropertyValue(123L), memgraph::storage::PropertyValue(123.5),
|
||||
memgraph::storage::PropertyValue("nandare"),
|
||||
memgraph::storage::PropertyValue{
|
||||
std::map<std::string, memgraph::storage::PropertyValue>{{"haihai", memgraph::storage::PropertyValue()}}},
|
||||
memgraph::storage::PropertyValue(memgraph::storage::TemporalData(memgraph::storage::TemporalType::Date, 23))}));
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST_F(DecoderEncoderTest, PropertyValueInvalidMarker) {
|
||||
{
|
||||
memgraph::storage::durability::Encoder encoder;
|
||||
encoder.Initialize(storage_file, kTestMagic, kTestVersion);
|
||||
encoder.WritePropertyValue(memgraph::storage::PropertyValue(123L));
|
||||
encoder.Finalize();
|
||||
}
|
||||
{
|
||||
memgraph::utils::OutputFile file;
|
||||
file.Open(storage_file, memgraph::utils::OutputFile::Mode::OVERWRITE_EXISTING);
|
||||
for (auto marker : memgraph::storage::durability::kMarkersAll) {
|
||||
bool valid_marker;
|
||||
switch (marker) {
|
||||
case memgraph::storage::durability::Marker::TYPE_NULL:
|
||||
case memgraph::storage::durability::Marker::TYPE_BOOL:
|
||||
case memgraph::storage::durability::Marker::TYPE_INT:
|
||||
case memgraph::storage::durability::Marker::TYPE_DOUBLE:
|
||||
case memgraph::storage::durability::Marker::TYPE_STRING:
|
||||
case memgraph::storage::durability::Marker::TYPE_LIST:
|
||||
case memgraph::storage::durability::Marker::TYPE_MAP:
|
||||
case memgraph::storage::durability::Marker::TYPE_TEMPORAL_DATA:
|
||||
case memgraph::storage::durability::Marker::TYPE_PROPERTY_VALUE:
|
||||
valid_marker = true;
|
||||
break;
|
||||
|
||||
case memgraph::storage::durability::Marker::SECTION_VERTEX:
|
||||
case memgraph::storage::durability::Marker::SECTION_EDGE:
|
||||
case memgraph::storage::durability::Marker::SECTION_MAPPER:
|
||||
case memgraph::storage::durability::Marker::SECTION_METADATA:
|
||||
case memgraph::storage::durability::Marker::SECTION_INDICES:
|
||||
case memgraph::storage::durability::Marker::SECTION_CONSTRAINTS:
|
||||
case memgraph::storage::durability::Marker::SECTION_DELTA:
|
||||
case memgraph::storage::durability::Marker::SECTION_EPOCH_HISTORY:
|
||||
case memgraph::storage::durability::Marker::SECTION_OFFSETS:
|
||||
case memgraph::storage::durability::Marker::DELTA_VERTEX_CREATE:
|
||||
case memgraph::storage::durability::Marker::DELTA_VERTEX_DELETE:
|
||||
case memgraph::storage::durability::Marker::DELTA_VERTEX_ADD_LABEL:
|
||||
case memgraph::storage::durability::Marker::DELTA_VERTEX_REMOVE_LABEL:
|
||||
case memgraph::storage::durability::Marker::DELTA_VERTEX_SET_PROPERTY:
|
||||
case memgraph::storage::durability::Marker::DELTA_EDGE_CREATE:
|
||||
case memgraph::storage::durability::Marker::DELTA_EDGE_DELETE:
|
||||
case memgraph::storage::durability::Marker::DELTA_EDGE_SET_PROPERTY:
|
||||
case memgraph::storage::durability::Marker::DELTA_TRANSACTION_END:
|
||||
case memgraph::storage::durability::Marker::DELTA_LABEL_INDEX_CREATE:
|
||||
case memgraph::storage::durability::Marker::DELTA_LABEL_INDEX_DROP:
|
||||
case memgraph::storage::durability::Marker::DELTA_LABEL_PROPERTY_INDEX_CREATE:
|
||||
case memgraph::storage::durability::Marker::DELTA_LABEL_PROPERTY_INDEX_DROP:
|
||||
case memgraph::storage::durability::Marker::DELTA_EXISTENCE_CONSTRAINT_CREATE:
|
||||
case memgraph::storage::durability::Marker::DELTA_EXISTENCE_CONSTRAINT_DROP:
|
||||
case memgraph::storage::durability::Marker::DELTA_UNIQUE_CONSTRAINT_CREATE:
|
||||
case memgraph::storage::durability::Marker::DELTA_UNIQUE_CONSTRAINT_DROP:
|
||||
case memgraph::storage::durability::Marker::VALUE_FALSE:
|
||||
case memgraph::storage::durability::Marker::VALUE_TRUE:
|
||||
valid_marker = false;
|
||||
break;
|
||||
}
|
||||
// We only run this test with invalid markers.
|
||||
if (valid_marker) continue;
|
||||
{
|
||||
file.SetPosition(memgraph::utils::OutputFile::Position::RELATIVE_TO_END,
|
||||
-(sizeof(uint64_t) + sizeof(memgraph::storage::durability::Marker)));
|
||||
auto byte = static_cast<uint8_t>(marker);
|
||||
file.Write(&byte, sizeof(byte));
|
||||
file.Sync();
|
||||
}
|
||||
{
|
||||
memgraph::storage::durability::Decoder decoder;
|
||||
auto version = decoder.Initialize(storage_file, kTestMagic);
|
||||
ASSERT_TRUE(version);
|
||||
ASSERT_EQ(*version, kTestVersion);
|
||||
ASSERT_FALSE(decoder.SkipPropertyValue());
|
||||
}
|
||||
{
|
||||
memgraph::storage::durability::Decoder decoder;
|
||||
auto version = decoder.Initialize(storage_file, kTestMagic);
|
||||
ASSERT_TRUE(version);
|
||||
ASSERT_EQ(*version, kTestVersion);
|
||||
ASSERT_FALSE(decoder.ReadPropertyValue());
|
||||
}
|
||||
}
|
||||
{
|
||||
{
|
||||
file.SetPosition(memgraph::utils::OutputFile::Position::RELATIVE_TO_END,
|
||||
-(sizeof(uint64_t) + sizeof(memgraph::storage::durability::Marker)));
|
||||
uint8_t byte = 1;
|
||||
file.Write(&byte, sizeof(byte));
|
||||
file.Sync();
|
||||
}
|
||||
{
|
||||
memgraph::storage::durability::Decoder decoder;
|
||||
auto version = decoder.Initialize(storage_file, kTestMagic);
|
||||
ASSERT_TRUE(version);
|
||||
ASSERT_EQ(*version, kTestVersion);
|
||||
ASSERT_FALSE(decoder.SkipPropertyValue());
|
||||
}
|
||||
{
|
||||
memgraph::storage::durability::Decoder decoder;
|
||||
auto version = decoder.Initialize(storage_file, kTestMagic);
|
||||
ASSERT_TRUE(version);
|
||||
ASSERT_EQ(*version, kTestVersion);
|
||||
ASSERT_FALSE(decoder.ReadPropertyValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST_F(DecoderEncoderTest, DecoderPosition) {
|
||||
{
|
||||
memgraph::storage::durability::Encoder encoder;
|
||||
encoder.Initialize(storage_file, kTestMagic, kTestVersion);
|
||||
encoder.WriteBool(true);
|
||||
encoder.Finalize();
|
||||
}
|
||||
{
|
||||
memgraph::storage::durability::Decoder decoder;
|
||||
auto version = decoder.Initialize(storage_file, kTestMagic);
|
||||
ASSERT_TRUE(version);
|
||||
ASSERT_EQ(*version, kTestVersion);
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
ASSERT_TRUE(decoder.SetPosition(kTestMagic.size() + sizeof(kTestVersion)));
|
||||
auto decoded = decoder.ReadBool();
|
||||
ASSERT_TRUE(decoded);
|
||||
ASSERT_TRUE(*decoded);
|
||||
auto pos = decoder.GetPosition();
|
||||
ASSERT_TRUE(pos);
|
||||
ASSERT_EQ(pos, decoder.GetSize());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST_F(DecoderEncoderTest, EncoderPosition) {
|
||||
{
|
||||
memgraph::storage::durability::Encoder encoder;
|
||||
encoder.Initialize(storage_file, kTestMagic, kTestVersion);
|
||||
encoder.WriteBool(false);
|
||||
encoder.SetPosition(kTestMagic.size() + sizeof(kTestVersion));
|
||||
ASSERT_EQ(encoder.GetPosition(), kTestMagic.size() + sizeof(kTestVersion));
|
||||
encoder.WriteBool(true);
|
||||
encoder.Finalize();
|
||||
}
|
||||
{
|
||||
memgraph::storage::durability::Decoder decoder;
|
||||
auto version = decoder.Initialize(storage_file, kTestMagic);
|
||||
ASSERT_TRUE(version);
|
||||
ASSERT_EQ(*version, kTestVersion);
|
||||
auto decoded = decoder.ReadBool();
|
||||
ASSERT_TRUE(decoded);
|
||||
ASSERT_TRUE(*decoded);
|
||||
auto pos = decoder.GetPosition();
|
||||
ASSERT_TRUE(pos);
|
||||
ASSERT_EQ(pos, decoder.GetSize());
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,200 +0,0 @@
|
||||
// Copyright 2022 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 "storage/v2/storage.hpp"
|
||||
|
||||
using testing::UnorderedElementsAre;
|
||||
|
||||
// TODO: The point of these is not to test GC fully, these are just simple
|
||||
// sanity checks. These will be superseded by a more sophisticated stress test
|
||||
// which will verify that GC is working properly in a multithreaded environment.
|
||||
|
||||
// A simple test trying to get GC to run while a transaction is still alive and
|
||||
// then verify that GC didn't delete anything it shouldn't have.
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST(StorageV2Gc, Sanity) {
|
||||
memgraph::storage::Storage storage(memgraph::storage::Config{
|
||||
.gc = {.type = memgraph::storage::Config::Gc::Type::PERIODIC, .interval = std::chrono::milliseconds(100)}});
|
||||
|
||||
std::vector<memgraph::storage::Gid> vertices;
|
||||
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
// Create some vertices, but delete some of them immediately.
|
||||
for (uint64_t i = 0; i < 1000; ++i) {
|
||||
auto vertex = acc.CreateVertex();
|
||||
vertices.push_back(vertex.Gid());
|
||||
}
|
||||
|
||||
acc.AdvanceCommand();
|
||||
|
||||
for (uint64_t i = 0; i < 1000; ++i) {
|
||||
auto vertex = acc.FindVertex(vertices[i], memgraph::storage::View::OLD);
|
||||
ASSERT_TRUE(vertex.has_value());
|
||||
if (i % 5 == 0) {
|
||||
EXPECT_FALSE(acc.DeleteVertex(&vertex.value()).HasError());
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for GC.
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(300));
|
||||
|
||||
for (uint64_t i = 0; i < 1000; ++i) {
|
||||
auto vertex_old = acc.FindVertex(vertices[i], memgraph::storage::View::OLD);
|
||||
auto vertex_new = acc.FindVertex(vertices[i], memgraph::storage::View::NEW);
|
||||
EXPECT_TRUE(vertex_old.has_value());
|
||||
EXPECT_EQ(vertex_new.has_value(), i % 5 != 0);
|
||||
}
|
||||
|
||||
ASSERT_FALSE(acc.Commit().HasError());
|
||||
}
|
||||
|
||||
// Verify existing vertices and add labels to some of them.
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
for (uint64_t i = 0; i < 1000; ++i) {
|
||||
auto vertex = acc.FindVertex(vertices[i], memgraph::storage::View::OLD);
|
||||
EXPECT_EQ(vertex.has_value(), i % 5 != 0);
|
||||
|
||||
if (vertex.has_value()) {
|
||||
EXPECT_FALSE(vertex->AddLabel(memgraph::storage::LabelId::FromUint(3 * i)).HasError());
|
||||
EXPECT_FALSE(vertex->AddLabel(memgraph::storage::LabelId::FromUint(3 * i + 1)).HasError());
|
||||
EXPECT_FALSE(vertex->AddLabel(memgraph::storage::LabelId::FromUint(3 * i + 2)).HasError());
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for GC.
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(300));
|
||||
|
||||
// Verify labels.
|
||||
for (uint64_t i = 0; i < 1000; ++i) {
|
||||
auto vertex = acc.FindVertex(vertices[i], memgraph::storage::View::NEW);
|
||||
EXPECT_EQ(vertex.has_value(), i % 5 != 0);
|
||||
|
||||
if (vertex.has_value()) {
|
||||
auto labels_old = vertex->Labels(memgraph::storage::View::OLD);
|
||||
EXPECT_TRUE(labels_old.HasValue());
|
||||
EXPECT_TRUE(labels_old->empty());
|
||||
|
||||
auto labels_new = vertex->Labels(memgraph::storage::View::NEW);
|
||||
EXPECT_TRUE(labels_new.HasValue());
|
||||
EXPECT_THAT(labels_new.GetValue(), UnorderedElementsAre(memgraph::storage::LabelId::FromUint(3 * i),
|
||||
memgraph::storage::LabelId::FromUint(3 * i + 1),
|
||||
memgraph::storage::LabelId::FromUint(3 * i + 2)));
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT_FALSE(acc.Commit().HasError());
|
||||
}
|
||||
|
||||
// Add and remove some edges.
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
for (uint64_t i = 0; i < 1000; ++i) {
|
||||
auto from_vertex = acc.FindVertex(vertices[i], memgraph::storage::View::OLD);
|
||||
auto to_vertex = acc.FindVertex(vertices[(i + 1) % 1000], memgraph::storage::View::OLD);
|
||||
EXPECT_EQ(from_vertex.has_value(), i % 5 != 0);
|
||||
EXPECT_EQ(to_vertex.has_value(), (i + 1) % 5 != 0);
|
||||
|
||||
if (from_vertex.has_value() && to_vertex.has_value()) {
|
||||
EXPECT_FALSE(
|
||||
acc.CreateEdge(&from_vertex.value(), &to_vertex.value(), memgraph::storage::EdgeTypeId::FromUint(i))
|
||||
.HasError());
|
||||
}
|
||||
}
|
||||
|
||||
// Detach delete some vertices.
|
||||
for (uint64_t i = 0; i < 1000; ++i) {
|
||||
auto vertex = acc.FindVertex(vertices[i], memgraph::storage::View::NEW);
|
||||
EXPECT_EQ(vertex.has_value(), i % 5 != 0);
|
||||
if (vertex.has_value()) {
|
||||
if (i % 3 == 0) {
|
||||
EXPECT_FALSE(acc.DetachDeleteVertex(&vertex.value()).HasError());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for GC.
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(300));
|
||||
|
||||
// Vertify edges.
|
||||
for (uint64_t i = 0; i < 1000; ++i) {
|
||||
auto vertex = acc.FindVertex(vertices[i], memgraph::storage::View::NEW);
|
||||
EXPECT_EQ(vertex.has_value(), i % 5 != 0 && i % 3 != 0);
|
||||
if (vertex.has_value()) {
|
||||
auto out_edges = vertex->OutEdges(memgraph::storage::View::NEW);
|
||||
if (i % 5 != 4 && i % 3 != 2) {
|
||||
EXPECT_EQ(out_edges.GetValue().size(), 1);
|
||||
EXPECT_EQ(*vertex->OutDegree(memgraph::storage::View::NEW), 1);
|
||||
EXPECT_EQ(out_edges.GetValue().at(0).EdgeType().AsUint(), i);
|
||||
} else {
|
||||
EXPECT_TRUE(out_edges->empty());
|
||||
}
|
||||
|
||||
auto in_edges = vertex->InEdges(memgraph::storage::View::NEW);
|
||||
if (i % 5 != 1 && i % 3 != 1) {
|
||||
EXPECT_EQ(in_edges.GetValue().size(), 1);
|
||||
EXPECT_EQ(*vertex->InDegree(memgraph::storage::View::NEW), 1);
|
||||
EXPECT_EQ(in_edges.GetValue().at(0).EdgeType().AsUint(), (i + 999) % 1000);
|
||||
} else {
|
||||
EXPECT_TRUE(in_edges->empty());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT_FALSE(acc.Commit().HasError());
|
||||
}
|
||||
}
|
||||
|
||||
// A simple sanity check for index GC:
|
||||
// 1. Start transaction 0, create some vertices, add a label to them and
|
||||
// commit.
|
||||
// 2. Start transaction 1.
|
||||
// 3. Start transaction 2, remove the labels and commit;
|
||||
// 4. Wait for GC. GC shouldn't remove the vertices from index because
|
||||
// transaction 1 can still see them with that label.
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST(StorageV2Gc, Indices) {
|
||||
memgraph::storage::Storage storage(memgraph::storage::Config{
|
||||
.gc = {.type = memgraph::storage::Config::Gc::Type::PERIODIC, .interval = std::chrono::milliseconds(100)}});
|
||||
|
||||
ASSERT_TRUE(storage.CreateIndex(storage.NameToLabel("label")));
|
||||
|
||||
{
|
||||
auto acc0 = storage.Access();
|
||||
for (uint64_t i = 0; i < 1000; ++i) {
|
||||
auto vertex = acc0.CreateVertex();
|
||||
ASSERT_TRUE(*vertex.AddLabel(acc0.NameToLabel("label")));
|
||||
}
|
||||
ASSERT_FALSE(acc0.Commit().HasError());
|
||||
}
|
||||
{
|
||||
auto acc1 = storage.Access();
|
||||
|
||||
auto acc2 = storage.Access();
|
||||
for (auto vertex : acc2.Vertices(memgraph::storage::View::OLD)) {
|
||||
ASSERT_TRUE(*vertex.RemoveLabel(acc2.NameToLabel("label")));
|
||||
}
|
||||
ASSERT_FALSE(acc2.Commit().HasError());
|
||||
|
||||
// Wait for GC.
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(300));
|
||||
|
||||
std::set<memgraph::storage::Gid> gids;
|
||||
for (auto vertex : acc1.Vertices(acc1.NameToLabel("label"), memgraph::storage::View::OLD)) {
|
||||
gids.insert(vertex.Gid());
|
||||
}
|
||||
EXPECT_EQ(gids.size(), 1000);
|
||||
}
|
||||
}
|
@ -1,832 +0,0 @@
|
||||
// Copyright 2022 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 "storage/v2/property_value.hpp"
|
||||
#include "storage/v2/storage.hpp"
|
||||
#include "storage/v2/temporal.hpp"
|
||||
|
||||
// NOLINTNEXTLINE(google-build-using-namespace)
|
||||
using namespace memgraph::storage;
|
||||
|
||||
using testing::IsEmpty;
|
||||
using testing::UnorderedElementsAre;
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
|
||||
#define ASSERT_NO_ERROR(result) ASSERT_FALSE((result).HasError())
|
||||
|
||||
class IndexTest : public testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
auto acc = storage.Access();
|
||||
prop_id = acc.NameToProperty("id");
|
||||
prop_val = acc.NameToProperty("val");
|
||||
label1 = acc.NameToLabel("label1");
|
||||
label2 = acc.NameToLabel("label2");
|
||||
vertex_id = 0;
|
||||
}
|
||||
|
||||
Storage storage;
|
||||
PropertyId prop_id;
|
||||
PropertyId prop_val;
|
||||
LabelId label1;
|
||||
LabelId label2;
|
||||
|
||||
VertexAccessor CreateVertex(Storage::Accessor *accessor) {
|
||||
VertexAccessor vertex = accessor->CreateVertex();
|
||||
MG_ASSERT(!vertex.SetProperty(prop_id, PropertyValue(vertex_id++)).HasError());
|
||||
return vertex;
|
||||
}
|
||||
|
||||
template <class TIterable>
|
||||
std::vector<int64_t> GetIds(TIterable iterable, View view = View::OLD) {
|
||||
std::vector<int64_t> ret;
|
||||
for (auto vertex : iterable) {
|
||||
ret.push_back(vertex.GetProperty(prop_id, view)->ValueInt());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
private:
|
||||
int vertex_id;
|
||||
};
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST_F(IndexTest, LabelIndexCreate) {
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
EXPECT_FALSE(acc.LabelIndexExists(label1));
|
||||
}
|
||||
EXPECT_EQ(storage.ListAllIndices().label.size(), 0);
|
||||
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
auto vertex = CreateVertex(&acc);
|
||||
ASSERT_NO_ERROR(vertex.AddLabel(i % 2 ? label1 : label2));
|
||||
}
|
||||
ASSERT_NO_ERROR(acc.Commit());
|
||||
}
|
||||
|
||||
EXPECT_TRUE(storage.CreateIndex(label1));
|
||||
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, View::OLD), View::OLD), UnorderedElementsAre(1, 3, 5, 7, 9));
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, View::NEW), View::NEW), UnorderedElementsAre(1, 3, 5, 7, 9));
|
||||
}
|
||||
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
for (int i = 10; i < 20; ++i) {
|
||||
auto vertex = CreateVertex(&acc);
|
||||
ASSERT_NO_ERROR(vertex.AddLabel(i % 2 ? label1 : label2));
|
||||
}
|
||||
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, View::OLD), View::OLD), UnorderedElementsAre(1, 3, 5, 7, 9));
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, View::NEW), View::NEW),
|
||||
UnorderedElementsAre(1, 3, 5, 7, 9, 11, 13, 15, 17, 19));
|
||||
|
||||
acc.AdvanceCommand();
|
||||
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, View::OLD), View::NEW),
|
||||
UnorderedElementsAre(1, 3, 5, 7, 9, 11, 13, 15, 17, 19));
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, View::NEW), View::NEW),
|
||||
UnorderedElementsAre(1, 3, 5, 7, 9, 11, 13, 15, 17, 19));
|
||||
|
||||
acc.Abort();
|
||||
}
|
||||
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
for (int i = 10; i < 20; ++i) {
|
||||
auto vertex = CreateVertex(&acc);
|
||||
ASSERT_NO_ERROR(vertex.AddLabel(i % 2 ? label1 : label2));
|
||||
}
|
||||
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, View::OLD), View::OLD), UnorderedElementsAre(1, 3, 5, 7, 9));
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, View::NEW), View::NEW),
|
||||
UnorderedElementsAre(1, 3, 5, 7, 9, 21, 23, 25, 27, 29));
|
||||
|
||||
acc.AdvanceCommand();
|
||||
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, View::OLD), View::NEW),
|
||||
UnorderedElementsAre(1, 3, 5, 7, 9, 21, 23, 25, 27, 29));
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, View::NEW), View::NEW),
|
||||
UnorderedElementsAre(1, 3, 5, 7, 9, 21, 23, 25, 27, 29));
|
||||
|
||||
ASSERT_NO_ERROR(acc.Commit());
|
||||
}
|
||||
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, View::OLD), View::OLD),
|
||||
UnorderedElementsAre(1, 3, 5, 7, 9, 21, 23, 25, 27, 29));
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, View::NEW), View::NEW),
|
||||
UnorderedElementsAre(1, 3, 5, 7, 9, 21, 23, 25, 27, 29));
|
||||
|
||||
acc.AdvanceCommand();
|
||||
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, View::OLD), View::NEW),
|
||||
UnorderedElementsAre(1, 3, 5, 7, 9, 21, 23, 25, 27, 29));
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, View::NEW), View::NEW),
|
||||
UnorderedElementsAre(1, 3, 5, 7, 9, 21, 23, 25, 27, 29));
|
||||
|
||||
ASSERT_NO_ERROR(acc.Commit());
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST_F(IndexTest, LabelIndexDrop) {
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
EXPECT_FALSE(acc.LabelIndexExists(label1));
|
||||
}
|
||||
EXPECT_EQ(storage.ListAllIndices().label.size(), 0);
|
||||
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
auto vertex = CreateVertex(&acc);
|
||||
ASSERT_NO_ERROR(vertex.AddLabel(i % 2 ? label1 : label2));
|
||||
}
|
||||
ASSERT_NO_ERROR(acc.Commit());
|
||||
}
|
||||
|
||||
EXPECT_TRUE(storage.CreateIndex(label1));
|
||||
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, View::OLD), View::OLD), UnorderedElementsAre(1, 3, 5, 7, 9));
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, View::NEW), View::NEW), UnorderedElementsAre(1, 3, 5, 7, 9));
|
||||
}
|
||||
|
||||
EXPECT_TRUE(storage.DropIndex(label1));
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
EXPECT_FALSE(acc.LabelIndexExists(label1));
|
||||
}
|
||||
EXPECT_EQ(storage.ListAllIndices().label.size(), 0);
|
||||
|
||||
EXPECT_FALSE(storage.DropIndex(label1));
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
EXPECT_FALSE(acc.LabelIndexExists(label1));
|
||||
}
|
||||
EXPECT_EQ(storage.ListAllIndices().label.size(), 0);
|
||||
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
for (int i = 10; i < 20; ++i) {
|
||||
auto vertex = CreateVertex(&acc);
|
||||
ASSERT_NO_ERROR(vertex.AddLabel(i % 2 ? label1 : label2));
|
||||
}
|
||||
ASSERT_NO_ERROR(acc.Commit());
|
||||
}
|
||||
|
||||
EXPECT_TRUE(storage.CreateIndex(label1));
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
EXPECT_TRUE(acc.LabelIndexExists(label1));
|
||||
}
|
||||
EXPECT_THAT(storage.ListAllIndices().label, UnorderedElementsAre(label1));
|
||||
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, View::OLD), View::OLD),
|
||||
UnorderedElementsAre(1, 3, 5, 7, 9, 11, 13, 15, 17, 19));
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, View::NEW), View::NEW),
|
||||
UnorderedElementsAre(1, 3, 5, 7, 9, 11, 13, 15, 17, 19));
|
||||
|
||||
acc.AdvanceCommand();
|
||||
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, View::OLD), View::NEW),
|
||||
UnorderedElementsAre(1, 3, 5, 7, 9, 11, 13, 15, 17, 19));
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, View::NEW), View::NEW),
|
||||
UnorderedElementsAre(1, 3, 5, 7, 9, 11, 13, 15, 17, 19));
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST_F(IndexTest, LabelIndexBasic) {
|
||||
// The following steps are performed and index correctness is validated after
|
||||
// each step:
|
||||
// 1. Create 10 vertices numbered from 0 to 9.
|
||||
// 2. Add Label1 to odd numbered, and Label2 to even numbered vertices.
|
||||
// 3. Remove Label1 from odd numbered vertices, and add it to even numbered
|
||||
// vertices.
|
||||
// 4. Delete even numbered vertices.
|
||||
EXPECT_TRUE(storage.CreateIndex(label1));
|
||||
EXPECT_TRUE(storage.CreateIndex(label2));
|
||||
|
||||
auto acc = storage.Access();
|
||||
EXPECT_THAT(storage.ListAllIndices().label, UnorderedElementsAre(label1, label2));
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, View::OLD), View::OLD), IsEmpty());
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label2, View::OLD), View::OLD), IsEmpty());
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, View::NEW), View::NEW), IsEmpty());
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label2, View::NEW), View::NEW), IsEmpty());
|
||||
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
auto vertex = CreateVertex(&acc);
|
||||
ASSERT_NO_ERROR(vertex.AddLabel(i % 2 ? label1 : label2));
|
||||
}
|
||||
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, View::OLD), View::OLD), IsEmpty());
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label2, View::OLD), View::OLD), IsEmpty());
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, View::NEW), View::NEW), UnorderedElementsAre(1, 3, 5, 7, 9));
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label2, View::NEW), View::NEW), UnorderedElementsAre(0, 2, 4, 6, 8));
|
||||
|
||||
acc.AdvanceCommand();
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, View::OLD), View::OLD), UnorderedElementsAre(1, 3, 5, 7, 9));
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label2, View::OLD), View::OLD), UnorderedElementsAre(0, 2, 4, 6, 8));
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, View::NEW), View::NEW), UnorderedElementsAre(1, 3, 5, 7, 9));
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label2, View::NEW), View::NEW), UnorderedElementsAre(0, 2, 4, 6, 8));
|
||||
|
||||
for (auto vertex : acc.Vertices(View::OLD)) {
|
||||
int64_t id = vertex.GetProperty(prop_id, View::OLD)->ValueInt();
|
||||
if (id % 2) {
|
||||
ASSERT_NO_ERROR(vertex.RemoveLabel(label1));
|
||||
} else {
|
||||
ASSERT_NO_ERROR(vertex.AddLabel(label1));
|
||||
}
|
||||
}
|
||||
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, View::OLD), View::OLD), UnorderedElementsAre(1, 3, 5, 7, 9));
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label2, View::OLD), View::OLD), UnorderedElementsAre(0, 2, 4, 6, 8));
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, View::NEW), View::NEW), UnorderedElementsAre(0, 2, 4, 6, 8));
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label2, View::NEW), View::NEW), UnorderedElementsAre(0, 2, 4, 6, 8));
|
||||
|
||||
for (auto vertex : acc.Vertices(View::OLD)) {
|
||||
int64_t id = vertex.GetProperty(prop_id, View::OLD)->ValueInt();
|
||||
if (id % 2 == 0) {
|
||||
ASSERT_NO_ERROR(acc.DeleteVertex(&vertex));
|
||||
}
|
||||
}
|
||||
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, View::OLD), View::OLD), UnorderedElementsAre(1, 3, 5, 7, 9));
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label2, View::OLD), View::OLD), UnorderedElementsAre(0, 2, 4, 6, 8));
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, View::NEW), View::NEW), IsEmpty());
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label2, View::NEW), View::NEW), IsEmpty());
|
||||
|
||||
acc.AdvanceCommand();
|
||||
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, View::OLD), View::OLD), IsEmpty());
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label2, View::OLD), View::OLD), IsEmpty());
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, View::NEW), View::NEW), IsEmpty());
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label2, View::NEW), View::NEW), IsEmpty());
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST_F(IndexTest, LabelIndexDuplicateVersions) {
|
||||
// By removing labels and adding them again we create duplicate entries for
|
||||
// the same vertex in the index (they only differ by the timestamp). This test
|
||||
// checks that duplicates are properly filtered out.
|
||||
EXPECT_TRUE(storage.CreateIndex(label1));
|
||||
EXPECT_TRUE(storage.CreateIndex(label2));
|
||||
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
auto vertex = CreateVertex(&acc);
|
||||
ASSERT_NO_ERROR(vertex.AddLabel(label1));
|
||||
}
|
||||
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, View::NEW), View::NEW), UnorderedElementsAre(0, 1, 2, 3, 4));
|
||||
|
||||
ASSERT_NO_ERROR(acc.Commit());
|
||||
}
|
||||
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, View::OLD), View::OLD), UnorderedElementsAre(0, 1, 2, 3, 4));
|
||||
|
||||
for (auto vertex : acc.Vertices(View::OLD)) {
|
||||
ASSERT_NO_ERROR(vertex.RemoveLabel(label1));
|
||||
}
|
||||
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, View::OLD), View::OLD), UnorderedElementsAre(0, 1, 2, 3, 4));
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, View::NEW), View::NEW), IsEmpty());
|
||||
|
||||
for (auto vertex : acc.Vertices(View::OLD)) {
|
||||
ASSERT_NO_ERROR(vertex.AddLabel(label1));
|
||||
}
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, View::OLD), View::OLD), UnorderedElementsAre(0, 1, 2, 3, 4));
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, View::NEW), View::NEW), UnorderedElementsAre(0, 1, 2, 3, 4));
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST_F(IndexTest, LabelIndexTransactionalIsolation) {
|
||||
// Check that transactions only see entries they are supposed to see.
|
||||
EXPECT_TRUE(storage.CreateIndex(label1));
|
||||
EXPECT_TRUE(storage.CreateIndex(label2));
|
||||
|
||||
auto acc_before = storage.Access();
|
||||
auto acc = storage.Access();
|
||||
auto acc_after = storage.Access();
|
||||
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
auto vertex = CreateVertex(&acc);
|
||||
ASSERT_NO_ERROR(vertex.AddLabel(label1));
|
||||
}
|
||||
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, View::NEW), View::NEW), UnorderedElementsAre(0, 1, 2, 3, 4));
|
||||
EXPECT_THAT(GetIds(acc_before.Vertices(label1, View::NEW), View::NEW), IsEmpty());
|
||||
EXPECT_THAT(GetIds(acc_after.Vertices(label1, View::NEW), View::NEW), IsEmpty());
|
||||
|
||||
ASSERT_NO_ERROR(acc.Commit());
|
||||
|
||||
auto acc_after_commit = storage.Access();
|
||||
|
||||
EXPECT_THAT(GetIds(acc_before.Vertices(label1, View::NEW), View::NEW), IsEmpty());
|
||||
EXPECT_THAT(GetIds(acc_after.Vertices(label1, View::NEW), View::NEW), IsEmpty());
|
||||
EXPECT_THAT(GetIds(acc_after_commit.Vertices(label1, View::NEW), View::NEW), UnorderedElementsAre(0, 1, 2, 3, 4));
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST_F(IndexTest, LabelIndexCountEstimate) {
|
||||
EXPECT_TRUE(storage.CreateIndex(label1));
|
||||
EXPECT_TRUE(storage.CreateIndex(label2));
|
||||
|
||||
auto acc = storage.Access();
|
||||
for (int i = 0; i < 20; ++i) {
|
||||
auto vertex = CreateVertex(&acc);
|
||||
ASSERT_NO_ERROR(vertex.AddLabel(i % 3 ? label1 : label2));
|
||||
}
|
||||
|
||||
EXPECT_EQ(acc.ApproximateVertexCount(label1), 13);
|
||||
EXPECT_EQ(acc.ApproximateVertexCount(label2), 7);
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST_F(IndexTest, LabelPropertyIndexCreateAndDrop) {
|
||||
EXPECT_EQ(storage.ListAllIndices().label_property.size(), 0);
|
||||
EXPECT_TRUE(storage.CreateIndex(label1, prop_id));
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
EXPECT_TRUE(acc.LabelPropertyIndexExists(label1, prop_id));
|
||||
}
|
||||
EXPECT_THAT(storage.ListAllIndices().label_property, UnorderedElementsAre(std::make_pair(label1, prop_id)));
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
EXPECT_FALSE(acc.LabelPropertyIndexExists(label2, prop_id));
|
||||
}
|
||||
EXPECT_FALSE(storage.CreateIndex(label1, prop_id));
|
||||
EXPECT_THAT(storage.ListAllIndices().label_property, UnorderedElementsAre(std::make_pair(label1, prop_id)));
|
||||
|
||||
EXPECT_TRUE(storage.CreateIndex(label2, prop_id));
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
EXPECT_TRUE(acc.LabelPropertyIndexExists(label2, prop_id));
|
||||
}
|
||||
EXPECT_THAT(storage.ListAllIndices().label_property,
|
||||
UnorderedElementsAre(std::make_pair(label1, prop_id), std::make_pair(label2, prop_id)));
|
||||
|
||||
EXPECT_TRUE(storage.DropIndex(label1, prop_id));
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
EXPECT_FALSE(acc.LabelPropertyIndexExists(label1, prop_id));
|
||||
}
|
||||
EXPECT_THAT(storage.ListAllIndices().label_property, UnorderedElementsAre(std::make_pair(label2, prop_id)));
|
||||
EXPECT_FALSE(storage.DropIndex(label1, prop_id));
|
||||
|
||||
EXPECT_TRUE(storage.DropIndex(label2, prop_id));
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
EXPECT_FALSE(acc.LabelPropertyIndexExists(label2, prop_id));
|
||||
}
|
||||
EXPECT_EQ(storage.ListAllIndices().label_property.size(), 0);
|
||||
}
|
||||
|
||||
// The following three tests are almost an exact copy-paste of the corresponding
|
||||
// label index tests. We request all vertices with given label and property from
|
||||
// the index, without range filtering. Range filtering is tested in a separate
|
||||
// test.
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST_F(IndexTest, LabelPropertyIndexBasic) {
|
||||
storage.CreateIndex(label1, prop_val);
|
||||
storage.CreateIndex(label2, prop_val);
|
||||
|
||||
auto acc = storage.Access();
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, View::OLD), View::OLD), IsEmpty());
|
||||
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
auto vertex = CreateVertex(&acc);
|
||||
ASSERT_NO_ERROR(vertex.AddLabel(i % 2 ? label1 : label2));
|
||||
ASSERT_NO_ERROR(vertex.SetProperty(prop_val, PropertyValue(i)));
|
||||
}
|
||||
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, View::OLD), View::OLD), IsEmpty());
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label2, prop_val, View::OLD), View::OLD), IsEmpty());
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, View::NEW), View::NEW), UnorderedElementsAre(1, 3, 5, 7, 9));
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label2, prop_val, View::NEW), View::NEW), UnorderedElementsAre(0, 2, 4, 6, 8));
|
||||
|
||||
acc.AdvanceCommand();
|
||||
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, View::OLD), View::OLD), UnorderedElementsAre(1, 3, 5, 7, 9));
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label2, prop_val, View::OLD), View::OLD), UnorderedElementsAre(0, 2, 4, 6, 8));
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, View::NEW), View::NEW), UnorderedElementsAre(1, 3, 5, 7, 9));
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label2, prop_val, View::NEW), View::NEW), UnorderedElementsAre(0, 2, 4, 6, 8));
|
||||
|
||||
for (auto vertex : acc.Vertices(View::OLD)) {
|
||||
int64_t id = vertex.GetProperty(prop_id, View::OLD)->ValueInt();
|
||||
if (id % 2) {
|
||||
ASSERT_NO_ERROR(vertex.SetProperty(prop_val, PropertyValue()));
|
||||
} else {
|
||||
ASSERT_NO_ERROR(vertex.AddLabel(label1));
|
||||
}
|
||||
}
|
||||
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, View::OLD), View::OLD), UnorderedElementsAre(1, 3, 5, 7, 9));
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label2, prop_val, View::OLD), View::OLD), UnorderedElementsAre(0, 2, 4, 6, 8));
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, View::NEW), View::NEW), UnorderedElementsAre(0, 2, 4, 6, 8));
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label2, prop_val, View::NEW), View::NEW), UnorderedElementsAre(0, 2, 4, 6, 8));
|
||||
|
||||
for (auto vertex : acc.Vertices(View::OLD)) {
|
||||
int64_t id = vertex.GetProperty(prop_id, View::OLD)->ValueInt();
|
||||
if (id % 2 == 0) {
|
||||
ASSERT_NO_ERROR(acc.DeleteVertex(&vertex));
|
||||
}
|
||||
}
|
||||
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, View::OLD), View::OLD), UnorderedElementsAre(1, 3, 5, 7, 9));
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label2, prop_val, View::OLD), View::OLD), UnorderedElementsAre(0, 2, 4, 6, 8));
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, View::NEW), View::NEW), IsEmpty());
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label2, prop_val, View::NEW), View::NEW), IsEmpty());
|
||||
|
||||
acc.AdvanceCommand();
|
||||
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, View::OLD), View::OLD), IsEmpty());
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label2, prop_val, View::OLD), View::OLD), IsEmpty());
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, View::NEW), View::NEW), IsEmpty());
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label2, prop_val, View::NEW), View::NEW), IsEmpty());
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST_F(IndexTest, LabelPropertyIndexDuplicateVersions) {
|
||||
storage.CreateIndex(label1, prop_val);
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
auto vertex = CreateVertex(&acc);
|
||||
ASSERT_NO_ERROR(vertex.AddLabel(label1));
|
||||
ASSERT_NO_ERROR(vertex.SetProperty(prop_val, PropertyValue(i)));
|
||||
}
|
||||
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, View::NEW), View::NEW), UnorderedElementsAre(0, 1, 2, 3, 4));
|
||||
|
||||
ASSERT_NO_ERROR(acc.Commit());
|
||||
}
|
||||
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, View::OLD), View::OLD), UnorderedElementsAre(0, 1, 2, 3, 4));
|
||||
|
||||
for (auto vertex : acc.Vertices(View::OLD)) {
|
||||
ASSERT_NO_ERROR(vertex.SetProperty(prop_val, PropertyValue()));
|
||||
}
|
||||
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, View::OLD), View::OLD), UnorderedElementsAre(0, 1, 2, 3, 4));
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, View::NEW), View::NEW), IsEmpty());
|
||||
|
||||
for (auto vertex : acc.Vertices(View::OLD)) {
|
||||
ASSERT_NO_ERROR(vertex.SetProperty(prop_val, PropertyValue(42)));
|
||||
}
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, View::OLD), View::OLD), UnorderedElementsAre(0, 1, 2, 3, 4));
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, View::NEW), View::NEW), UnorderedElementsAre(0, 1, 2, 3, 4));
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST_F(IndexTest, LabelPropertyIndexTransactionalIsolation) {
|
||||
storage.CreateIndex(label1, prop_val);
|
||||
|
||||
auto acc_before = storage.Access();
|
||||
auto acc = storage.Access();
|
||||
auto acc_after = storage.Access();
|
||||
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
auto vertex = CreateVertex(&acc);
|
||||
ASSERT_NO_ERROR(vertex.AddLabel(label1));
|
||||
ASSERT_NO_ERROR(vertex.SetProperty(prop_val, PropertyValue(i)));
|
||||
}
|
||||
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, View::NEW), View::NEW), UnorderedElementsAre(0, 1, 2, 3, 4));
|
||||
EXPECT_THAT(GetIds(acc_before.Vertices(label1, prop_val, View::NEW), View::NEW), IsEmpty());
|
||||
EXPECT_THAT(GetIds(acc_after.Vertices(label1, prop_val, View::NEW), View::NEW), IsEmpty());
|
||||
|
||||
ASSERT_NO_ERROR(acc.Commit());
|
||||
|
||||
auto acc_after_commit = storage.Access();
|
||||
|
||||
EXPECT_THAT(GetIds(acc_before.Vertices(label1, prop_val, View::NEW), View::NEW), IsEmpty());
|
||||
EXPECT_THAT(GetIds(acc_after.Vertices(label1, prop_val, View::NEW), View::NEW), IsEmpty());
|
||||
EXPECT_THAT(GetIds(acc_after_commit.Vertices(label1, prop_val, View::NEW), View::NEW),
|
||||
UnorderedElementsAre(0, 1, 2, 3, 4));
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST_F(IndexTest, LabelPropertyIndexFiltering) {
|
||||
// We insert vertices with values:
|
||||
// 0 0.0 1 1.0 2 2.0 3 3.0 4 4.0
|
||||
// Then we check all combinations of inclusive and exclusive bounds.
|
||||
// We also have a mix of doubles and integers to verify that they are sorted
|
||||
// properly.
|
||||
|
||||
storage.CreateIndex(label1, prop_val);
|
||||
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
auto vertex = CreateVertex(&acc);
|
||||
ASSERT_NO_ERROR(vertex.AddLabel(label1));
|
||||
ASSERT_NO_ERROR(vertex.SetProperty(prop_val, i % 2 ? PropertyValue(i / 2) : PropertyValue(i / 2.0)));
|
||||
}
|
||||
ASSERT_NO_ERROR(acc.Commit());
|
||||
}
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, PropertyValue(i), View::OLD)),
|
||||
UnorderedElementsAre(2 * i, 2 * i + 1));
|
||||
}
|
||||
|
||||
// [1, +inf>
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, memgraph::utils::MakeBoundInclusive(PropertyValue(1)),
|
||||
std::nullopt, View::OLD)),
|
||||
UnorderedElementsAre(2, 3, 4, 5, 6, 7, 8, 9));
|
||||
// <1, +inf>
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, memgraph::utils::MakeBoundExclusive(PropertyValue(1)),
|
||||
std::nullopt, View::OLD)),
|
||||
UnorderedElementsAre(4, 5, 6, 7, 8, 9));
|
||||
|
||||
// <-inf, 3]
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, std::nullopt,
|
||||
memgraph::utils::MakeBoundInclusive(PropertyValue(3)), View::OLD)),
|
||||
UnorderedElementsAre(0, 1, 2, 3, 4, 5, 6, 7));
|
||||
// <-inf, 3>
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, std::nullopt,
|
||||
memgraph::utils::MakeBoundExclusive(PropertyValue(3)), View::OLD)),
|
||||
UnorderedElementsAre(0, 1, 2, 3, 4, 5));
|
||||
|
||||
// [1, 3]
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, memgraph::utils::MakeBoundInclusive(PropertyValue(1)),
|
||||
memgraph::utils::MakeBoundInclusive(PropertyValue(3)), View::OLD)),
|
||||
UnorderedElementsAre(2, 3, 4, 5, 6, 7));
|
||||
// <1, 3]
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, memgraph::utils::MakeBoundExclusive(PropertyValue(1)),
|
||||
memgraph::utils::MakeBoundInclusive(PropertyValue(3)), View::OLD)),
|
||||
UnorderedElementsAre(4, 5, 6, 7));
|
||||
// [1, 3>
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, memgraph::utils::MakeBoundInclusive(PropertyValue(1)),
|
||||
memgraph::utils::MakeBoundExclusive(PropertyValue(3)), View::OLD)),
|
||||
UnorderedElementsAre(2, 3, 4, 5));
|
||||
// <1, 3>
|
||||
EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, memgraph::utils::MakeBoundExclusive(PropertyValue(1)),
|
||||
memgraph::utils::MakeBoundExclusive(PropertyValue(3)), View::OLD)),
|
||||
UnorderedElementsAre(4, 5));
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST_F(IndexTest, LabelPropertyIndexCountEstimate) {
|
||||
storage.CreateIndex(label1, prop_val);
|
||||
|
||||
auto acc = storage.Access();
|
||||
for (int i = 1; i <= 10; ++i) {
|
||||
for (int j = 0; j < i; ++j) {
|
||||
auto vertex = CreateVertex(&acc);
|
||||
ASSERT_NO_ERROR(vertex.AddLabel(label1));
|
||||
ASSERT_NO_ERROR(vertex.SetProperty(prop_val, PropertyValue(i)));
|
||||
}
|
||||
}
|
||||
|
||||
EXPECT_EQ(acc.ApproximateVertexCount(label1, prop_val), 55);
|
||||
for (int i = 1; i <= 10; ++i) {
|
||||
EXPECT_EQ(acc.ApproximateVertexCount(label1, prop_val, PropertyValue(i)), i);
|
||||
}
|
||||
|
||||
EXPECT_EQ(acc.ApproximateVertexCount(label1, prop_val, memgraph::utils::MakeBoundInclusive(PropertyValue(2)),
|
||||
memgraph::utils::MakeBoundInclusive(PropertyValue(6))),
|
||||
2 + 3 + 4 + 5 + 6);
|
||||
}
|
||||
|
||||
TEST_F(IndexTest, LabelPropertyIndexMixedIteration) {
|
||||
storage.CreateIndex(label1, prop_val);
|
||||
|
||||
const std::array temporals{TemporalData{TemporalType::Date, 23}, TemporalData{TemporalType::Date, 28},
|
||||
TemporalData{TemporalType::LocalDateTime, 20}};
|
||||
|
||||
std::vector<PropertyValue> values = {
|
||||
PropertyValue(false),
|
||||
PropertyValue(true),
|
||||
PropertyValue(-std::numeric_limits<double>::infinity()),
|
||||
PropertyValue(std::numeric_limits<int64_t>::min()),
|
||||
PropertyValue(-1),
|
||||
PropertyValue(-0.5),
|
||||
PropertyValue(0),
|
||||
PropertyValue(0.5),
|
||||
PropertyValue(1),
|
||||
PropertyValue(1.5),
|
||||
PropertyValue(2),
|
||||
PropertyValue(std::numeric_limits<int64_t>::max()),
|
||||
PropertyValue(std::numeric_limits<double>::infinity()),
|
||||
PropertyValue(""),
|
||||
PropertyValue("a"),
|
||||
PropertyValue("b"),
|
||||
PropertyValue("c"),
|
||||
PropertyValue(std::vector<PropertyValue>()),
|
||||
PropertyValue(std::vector<PropertyValue>{PropertyValue(0.8)}),
|
||||
PropertyValue(std::vector<PropertyValue>{PropertyValue(2)}),
|
||||
PropertyValue(std::map<std::string, PropertyValue>()),
|
||||
PropertyValue(std::map<std::string, PropertyValue>{{"id", PropertyValue(5)}}),
|
||||
PropertyValue(std::map<std::string, PropertyValue>{{"id", PropertyValue(10)}}),
|
||||
PropertyValue(temporals[0]),
|
||||
PropertyValue(temporals[1]),
|
||||
PropertyValue(temporals[2]),
|
||||
};
|
||||
|
||||
// Create vertices, each with one of the values above.
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
for (const auto &value : values) {
|
||||
auto v = acc.CreateVertex();
|
||||
ASSERT_TRUE(v.AddLabel(label1).HasValue());
|
||||
ASSERT_TRUE(v.SetProperty(prop_val, value).HasValue());
|
||||
}
|
||||
ASSERT_FALSE(acc.Commit().HasError());
|
||||
}
|
||||
|
||||
// Verify that all nodes are in the index.
|
||||
{
|
||||
auto acc = storage.Access();
|
||||
auto iterable = acc.Vertices(label1, prop_val, View::OLD);
|
||||
auto it = iterable.begin();
|
||||
for (const auto &value : values) {
|
||||
ASSERT_NE(it, iterable.end());
|
||||
auto vertex = *it;
|
||||
auto maybe_value = vertex.GetProperty(prop_val, View::OLD);
|
||||
ASSERT_TRUE(maybe_value.HasValue());
|
||||
ASSERT_EQ(value, *maybe_value);
|
||||
++it;
|
||||
}
|
||||
ASSERT_EQ(it, iterable.end());
|
||||
}
|
||||
|
||||
auto verify = [&](const std::optional<memgraph::utils::Bound<PropertyValue>> &from,
|
||||
const std::optional<memgraph::utils::Bound<PropertyValue>> &to,
|
||||
const std::vector<PropertyValue> &expected) {
|
||||
auto acc = storage.Access();
|
||||
auto iterable = acc.Vertices(label1, prop_val, from, to, View::OLD);
|
||||
size_t i = 0;
|
||||
for (auto it = iterable.begin(); it != iterable.end(); ++it, ++i) {
|
||||
auto vertex = *it;
|
||||
auto maybe_value = vertex.GetProperty(prop_val, View::OLD);
|
||||
ASSERT_TRUE(maybe_value.HasValue());
|
||||
ASSERT_EQ(*maybe_value, expected[i]);
|
||||
}
|
||||
ASSERT_EQ(i, expected.size());
|
||||
};
|
||||
|
||||
// Range iteration with two specified bounds that have the same type should
|
||||
// yield the naturally expected items.
|
||||
verify(memgraph::utils::MakeBoundExclusive(PropertyValue(false)),
|
||||
memgraph::utils::MakeBoundExclusive(PropertyValue(true)), {});
|
||||
verify(memgraph::utils::MakeBoundExclusive(PropertyValue(false)),
|
||||
memgraph::utils::MakeBoundInclusive(PropertyValue(true)), {PropertyValue(true)});
|
||||
verify(memgraph::utils::MakeBoundInclusive(PropertyValue(false)),
|
||||
memgraph::utils::MakeBoundExclusive(PropertyValue(true)), {PropertyValue(false)});
|
||||
verify(memgraph::utils::MakeBoundInclusive(PropertyValue(false)),
|
||||
memgraph::utils::MakeBoundInclusive(PropertyValue(true)), {PropertyValue(false), PropertyValue(true)});
|
||||
verify(memgraph::utils::MakeBoundExclusive(PropertyValue(0)), memgraph::utils::MakeBoundExclusive(PropertyValue(1.8)),
|
||||
{PropertyValue(0.5), PropertyValue(1), PropertyValue(1.5)});
|
||||
verify(memgraph::utils::MakeBoundExclusive(PropertyValue(0)), memgraph::utils::MakeBoundInclusive(PropertyValue(1.8)),
|
||||
{PropertyValue(0.5), PropertyValue(1), PropertyValue(1.5)});
|
||||
verify(memgraph::utils::MakeBoundInclusive(PropertyValue(0)), memgraph::utils::MakeBoundExclusive(PropertyValue(1.8)),
|
||||
{PropertyValue(0), PropertyValue(0.5), PropertyValue(1), PropertyValue(1.5)});
|
||||
verify(memgraph::utils::MakeBoundInclusive(PropertyValue(0)), memgraph::utils::MakeBoundInclusive(PropertyValue(1.8)),
|
||||
{PropertyValue(0), PropertyValue(0.5), PropertyValue(1), PropertyValue(1.5)});
|
||||
verify(memgraph::utils::MakeBoundExclusive(PropertyValue("b")),
|
||||
memgraph::utils::MakeBoundExclusive(PropertyValue("memgraph")), {PropertyValue("c")});
|
||||
verify(memgraph::utils::MakeBoundExclusive(PropertyValue("b")),
|
||||
memgraph::utils::MakeBoundInclusive(PropertyValue("memgraph")), {PropertyValue("c")});
|
||||
verify(memgraph::utils::MakeBoundInclusive(PropertyValue("b")),
|
||||
memgraph::utils::MakeBoundExclusive(PropertyValue("memgraph")), {PropertyValue("b"), PropertyValue("c")});
|
||||
verify(memgraph::utils::MakeBoundInclusive(PropertyValue("b")),
|
||||
memgraph::utils::MakeBoundInclusive(PropertyValue("memgraph")), {PropertyValue("b"), PropertyValue("c")});
|
||||
verify(memgraph::utils::MakeBoundExclusive(PropertyValue(std::vector<PropertyValue>{PropertyValue(0.8)})),
|
||||
memgraph::utils::MakeBoundExclusive(PropertyValue(std::vector<PropertyValue>{PropertyValue("b")})),
|
||||
{PropertyValue(std::vector<PropertyValue>{PropertyValue(2)})});
|
||||
verify(memgraph::utils::MakeBoundExclusive(PropertyValue(std::vector<PropertyValue>{PropertyValue(0.8)})),
|
||||
memgraph::utils::MakeBoundInclusive(PropertyValue(std::vector<PropertyValue>{PropertyValue("b")})),
|
||||
{PropertyValue(std::vector<PropertyValue>{PropertyValue(2)})});
|
||||
verify(memgraph::utils::MakeBoundInclusive(PropertyValue(std::vector<PropertyValue>{PropertyValue(0.8)})),
|
||||
memgraph::utils::MakeBoundExclusive(PropertyValue(std::vector<PropertyValue>{PropertyValue("b")})),
|
||||
{PropertyValue(std::vector<PropertyValue>{PropertyValue(0.8)}),
|
||||
PropertyValue(std::vector<PropertyValue>{PropertyValue(2)})});
|
||||
verify(memgraph::utils::MakeBoundInclusive(PropertyValue(std::vector<PropertyValue>{PropertyValue(0.8)})),
|
||||
memgraph::utils::MakeBoundInclusive(PropertyValue(std::vector<PropertyValue>{PropertyValue("b")})),
|
||||
{PropertyValue(std::vector<PropertyValue>{PropertyValue(0.8)}),
|
||||
PropertyValue(std::vector<PropertyValue>{PropertyValue(2)})});
|
||||
verify(memgraph::utils::MakeBoundExclusive(
|
||||
PropertyValue(std::map<std::string, PropertyValue>{{"id", PropertyValue(5.0)}})),
|
||||
memgraph::utils::MakeBoundExclusive(
|
||||
PropertyValue(std::map<std::string, PropertyValue>{{"id", PropertyValue("b")}})),
|
||||
{PropertyValue(std::map<std::string, PropertyValue>{{"id", PropertyValue(10)}})});
|
||||
verify(memgraph::utils::MakeBoundExclusive(
|
||||
PropertyValue(std::map<std::string, PropertyValue>{{"id", PropertyValue(5.0)}})),
|
||||
memgraph::utils::MakeBoundInclusive(
|
||||
PropertyValue(std::map<std::string, PropertyValue>{{"id", PropertyValue("b")}})),
|
||||
{PropertyValue(std::map<std::string, PropertyValue>{{"id", PropertyValue(10)}})});
|
||||
verify(memgraph::utils::MakeBoundInclusive(
|
||||
PropertyValue(std::map<std::string, PropertyValue>{{"id", PropertyValue(5.0)}})),
|
||||
memgraph::utils::MakeBoundExclusive(
|
||||
PropertyValue(std::map<std::string, PropertyValue>{{"id", PropertyValue("b")}})),
|
||||
{PropertyValue(std::map<std::string, PropertyValue>{{"id", PropertyValue(5)}}),
|
||||
PropertyValue(std::map<std::string, PropertyValue>{{"id", PropertyValue(10)}})});
|
||||
verify(memgraph::utils::MakeBoundInclusive(
|
||||
PropertyValue(std::map<std::string, PropertyValue>{{"id", PropertyValue(5.0)}})),
|
||||
memgraph::utils::MakeBoundInclusive(
|
||||
PropertyValue(std::map<std::string, PropertyValue>{{"id", PropertyValue("b")}})),
|
||||
{PropertyValue(std::map<std::string, PropertyValue>{{"id", PropertyValue(5)}}),
|
||||
PropertyValue(std::map<std::string, PropertyValue>{{"id", PropertyValue(10)}})});
|
||||
|
||||
verify(memgraph::utils::MakeBoundExclusive(PropertyValue(temporals[0])),
|
||||
memgraph::utils::MakeBoundInclusive(PropertyValue(TemporalData{TemporalType::Date, 200})),
|
||||
// LocalDateTime has a "higher" type number so it is not part of the range
|
||||
{PropertyValue(temporals[1])});
|
||||
verify(memgraph::utils::MakeBoundExclusive(PropertyValue(temporals[0])),
|
||||
memgraph::utils::MakeBoundInclusive(PropertyValue(temporals[2])),
|
||||
{PropertyValue(temporals[1]), PropertyValue(temporals[2])});
|
||||
verify(memgraph::utils::MakeBoundInclusive(PropertyValue(temporals[0])),
|
||||
memgraph::utils::MakeBoundExclusive(PropertyValue(temporals[2])),
|
||||
{PropertyValue(temporals[0]), PropertyValue(temporals[1])});
|
||||
verify(memgraph::utils::MakeBoundInclusive(PropertyValue(temporals[0])),
|
||||
memgraph::utils::MakeBoundInclusive(PropertyValue(temporals[2])),
|
||||
{PropertyValue(temporals[0]), PropertyValue(temporals[1]), PropertyValue(temporals[2])});
|
||||
|
||||
// Range iteration with one unspecified bound should only yield items that
|
||||
// have the same type as the specified bound.
|
||||
verify(memgraph::utils::MakeBoundInclusive(PropertyValue(false)), std::nullopt,
|
||||
{PropertyValue(false), PropertyValue(true)});
|
||||
verify(std::nullopt, memgraph::utils::MakeBoundExclusive(PropertyValue(true)), {PropertyValue(false)});
|
||||
verify(memgraph::utils::MakeBoundInclusive(PropertyValue(1)), std::nullopt,
|
||||
{PropertyValue(1), PropertyValue(1.5), PropertyValue(2), PropertyValue(std::numeric_limits<int64_t>::max()),
|
||||
PropertyValue(std::numeric_limits<double>::infinity())});
|
||||
verify(std::nullopt, memgraph::utils::MakeBoundExclusive(PropertyValue(0)),
|
||||
{PropertyValue(-std::numeric_limits<double>::infinity()), PropertyValue(std::numeric_limits<int64_t>::min()),
|
||||
PropertyValue(-1), PropertyValue(-0.5)});
|
||||
verify(memgraph::utils::MakeBoundInclusive(PropertyValue("b")), std::nullopt,
|
||||
{PropertyValue("b"), PropertyValue("c")});
|
||||
verify(std::nullopt, memgraph::utils::MakeBoundExclusive(PropertyValue("b")),
|
||||
{PropertyValue(""), PropertyValue("a")});
|
||||
verify(memgraph::utils::MakeBoundInclusive(PropertyValue(std::vector<PropertyValue>{PropertyValue(false)})),
|
||||
std::nullopt,
|
||||
{PropertyValue(std::vector<PropertyValue>{PropertyValue(0.8)}),
|
||||
PropertyValue(std::vector<PropertyValue>{PropertyValue(2)})});
|
||||
verify(std::nullopt, memgraph::utils::MakeBoundExclusive(PropertyValue(std::vector<PropertyValue>{PropertyValue(1)})),
|
||||
{PropertyValue(std::vector<PropertyValue>()), PropertyValue(std::vector<PropertyValue>{PropertyValue(0.8)})});
|
||||
verify(memgraph::utils::MakeBoundInclusive(
|
||||
PropertyValue(std::map<std::string, PropertyValue>{{"id", PropertyValue(false)}})),
|
||||
std::nullopt,
|
||||
{PropertyValue(std::map<std::string, PropertyValue>{{"id", PropertyValue(5)}}),
|
||||
PropertyValue(std::map<std::string, PropertyValue>{{"id", PropertyValue(10)}})});
|
||||
verify(std::nullopt,
|
||||
memgraph::utils::MakeBoundExclusive(
|
||||
PropertyValue(std::map<std::string, PropertyValue>{{"id", PropertyValue(7.5)}})),
|
||||
{PropertyValue(std::map<std::string, PropertyValue>()),
|
||||
PropertyValue(std::map<std::string, PropertyValue>{{"id", PropertyValue(5)}})});
|
||||
verify(memgraph::utils::MakeBoundInclusive(PropertyValue(TemporalData(TemporalType::Date, 10))), std::nullopt,
|
||||
{PropertyValue(temporals[0]), PropertyValue(temporals[1]), PropertyValue(temporals[2])});
|
||||
verify(std::nullopt, memgraph::utils::MakeBoundExclusive(PropertyValue(TemporalData(TemporalType::Duration, 0))),
|
||||
{PropertyValue(temporals[0]), PropertyValue(temporals[1]), PropertyValue(temporals[2])});
|
||||
|
||||
// Range iteration with two specified bounds that don't have the same type
|
||||
// should yield no items.
|
||||
for (size_t i = 0; i < values.size(); ++i) {
|
||||
for (size_t j = i; j < values.size(); ++j) {
|
||||
if (PropertyValue::AreComparableTypes(values[i].type(), values[j].type())) {
|
||||
verify(memgraph::utils::MakeBoundInclusive(values[i]), memgraph::utils::MakeBoundInclusive(values[j]),
|
||||
{values.begin() + i, values.begin() + j + 1});
|
||||
} else {
|
||||
verify(memgraph::utils::MakeBoundInclusive(values[i]), memgraph::utils::MakeBoundInclusive(values[j]), {});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Iteration without any bounds should return all items of the index.
|
||||
verify(std::nullopt, std::nullopt, values);
|
||||
}
|
@ -1,110 +0,0 @@
|
||||
// Copyright 2022 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 <gtest/gtest.h>
|
||||
|
||||
#include "storage/v2/isolation_level.hpp"
|
||||
#include "storage/v2/storage.hpp"
|
||||
|
||||
namespace {
|
||||
int64_t VerticesCount(memgraph::storage::Storage::Accessor &accessor) {
|
||||
int64_t count{0};
|
||||
for ([[maybe_unused]] const auto &vertex : accessor.Vertices(memgraph::storage::View::NEW)) {
|
||||
++count;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
inline constexpr std::array isolation_levels{memgraph::storage::IsolationLevel::SNAPSHOT_ISOLATION,
|
||||
memgraph::storage::IsolationLevel::READ_COMMITTED,
|
||||
memgraph::storage::IsolationLevel::READ_UNCOMMITTED};
|
||||
|
||||
std::string_view IsolationLevelToString(const memgraph::storage::IsolationLevel isolation_level) {
|
||||
switch (isolation_level) {
|
||||
case memgraph::storage::IsolationLevel::SNAPSHOT_ISOLATION:
|
||||
return "SNAPSHOT_ISOLATION";
|
||||
case memgraph::storage::IsolationLevel::READ_COMMITTED:
|
||||
return "READ_COMMITTED";
|
||||
case memgraph::storage::IsolationLevel::READ_UNCOMMITTED:
|
||||
return "READ_UNCOMMITTED";
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class StorageIsolationLevelTest : public ::testing::TestWithParam<memgraph::storage::IsolationLevel> {
|
||||
public:
|
||||
struct PrintToStringParamName {
|
||||
std::string operator()(const testing::TestParamInfo<memgraph::storage::IsolationLevel> &info) {
|
||||
return std::string(IsolationLevelToString(static_cast<memgraph::storage::IsolationLevel>(info.param)));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
TEST_P(StorageIsolationLevelTest, Visibility) {
|
||||
const auto default_isolation_level = GetParam();
|
||||
|
||||
for (const auto override_isolation_level : isolation_levels) {
|
||||
memgraph::storage::Storage storage{
|
||||
memgraph::storage::Config{.transaction = {.isolation_level = default_isolation_level}}};
|
||||
auto creator = storage.Access();
|
||||
auto default_isolation_level_reader = storage.Access();
|
||||
auto override_isolation_level_reader = storage.Access(override_isolation_level);
|
||||
|
||||
ASSERT_EQ(VerticesCount(default_isolation_level_reader), 0);
|
||||
ASSERT_EQ(VerticesCount(override_isolation_level_reader), 0);
|
||||
|
||||
static constexpr auto iteration_count = 10;
|
||||
{
|
||||
SCOPED_TRACE(fmt::format(
|
||||
"Visibility while the creator transaction is active "
|
||||
"(default isolation level = {}, override isolation level = {})",
|
||||
IsolationLevelToString(default_isolation_level), IsolationLevelToString(override_isolation_level)));
|
||||
for (size_t i = 1; i <= iteration_count; ++i) {
|
||||
creator.CreateVertex();
|
||||
|
||||
const auto check_vertices_count = [i](auto &accessor, const auto isolation_level) {
|
||||
const auto expected_count = isolation_level == memgraph::storage::IsolationLevel::READ_UNCOMMITTED ? i : 0;
|
||||
EXPECT_EQ(VerticesCount(accessor), expected_count);
|
||||
};
|
||||
check_vertices_count(default_isolation_level_reader, default_isolation_level);
|
||||
check_vertices_count(override_isolation_level_reader, override_isolation_level);
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT_FALSE(creator.Commit().HasError());
|
||||
{
|
||||
SCOPED_TRACE(fmt::format(
|
||||
"Visibility after the creator transaction is committed "
|
||||
"(default isolation level = {}, override isolation level = {})",
|
||||
IsolationLevelToString(default_isolation_level), IsolationLevelToString(override_isolation_level)));
|
||||
const auto check_vertices_count = [](auto &accessor, const auto isolation_level) {
|
||||
const auto expected_count =
|
||||
isolation_level == memgraph::storage::IsolationLevel::SNAPSHOT_ISOLATION ? 0 : iteration_count;
|
||||
ASSERT_EQ(VerticesCount(accessor), expected_count);
|
||||
};
|
||||
|
||||
check_vertices_count(default_isolation_level_reader, default_isolation_level);
|
||||
check_vertices_count(override_isolation_level_reader, override_isolation_level);
|
||||
}
|
||||
|
||||
ASSERT_FALSE(default_isolation_level_reader.Commit().HasError());
|
||||
ASSERT_FALSE(override_isolation_level_reader.Commit().HasError());
|
||||
|
||||
SCOPED_TRACE("Visibility after a new transaction is started");
|
||||
auto verifier = storage.Access();
|
||||
ASSERT_EQ(VerticesCount(verifier), iteration_count);
|
||||
ASSERT_FALSE(verifier.Commit().HasError());
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(ParameterizedStorageIsolationLevelTests, StorageIsolationLevelTest,
|
||||
::testing::ValuesIn(isolation_levels), StorageIsolationLevelTest::PrintToStringParamName());
|
@ -1,47 +0,0 @@
|
||||
// Copyright 2022 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 <gtest/gtest.h>
|
||||
|
||||
#include "storage/v2/name_id_mapper.hpp"
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST(NameIdMapper, Basic) {
|
||||
memgraph::storage::NameIdMapper mapper;
|
||||
|
||||
ASSERT_EQ(mapper.NameToId("n1"), 0);
|
||||
ASSERT_EQ(mapper.NameToId("n2"), 1);
|
||||
ASSERT_EQ(mapper.NameToId("n1"), 0);
|
||||
ASSERT_EQ(mapper.NameToId("n2"), 1);
|
||||
ASSERT_EQ(mapper.NameToId("n3"), 2);
|
||||
|
||||
ASSERT_EQ(mapper.IdToName(0), "n1");
|
||||
ASSERT_EQ(mapper.IdToName(1), "n2");
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST(NameIdMapper, Correctness) {
|
||||
memgraph::storage::NameIdMapper mapper;
|
||||
|
||||
ASSERT_DEATH(mapper.IdToName(0), "");
|
||||
ASSERT_EQ(mapper.NameToId("n1"), 0);
|
||||
ASSERT_EQ(mapper.IdToName(0), "n1");
|
||||
|
||||
ASSERT_DEATH(mapper.IdToName(1), "");
|
||||
ASSERT_EQ(mapper.NameToId("n2"), 1);
|
||||
ASSERT_EQ(mapper.IdToName(1), "n2");
|
||||
|
||||
ASSERT_EQ(mapper.NameToId("n1"), 0);
|
||||
ASSERT_EQ(mapper.NameToId("n2"), 1);
|
||||
|
||||
ASSERT_EQ(mapper.IdToName(1), "n2");
|
||||
ASSERT_EQ(mapper.IdToName(0), "n1");
|
||||
}
|
@ -1,651 +0,0 @@
|
||||
// Copyright 2022 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 <limits>
|
||||
|
||||
#include "storage/v2/property_store.hpp"
|
||||
#include "storage/v2/property_value.hpp"
|
||||
#include "storage/v2/temporal.hpp"
|
||||
|
||||
using testing::UnorderedElementsAre;
|
||||
|
||||
const memgraph::storage::PropertyValue kSampleValues[] = {
|
||||
memgraph::storage::PropertyValue(),
|
||||
memgraph::storage::PropertyValue(false),
|
||||
memgraph::storage::PropertyValue(true),
|
||||
memgraph::storage::PropertyValue(0),
|
||||
memgraph::storage::PropertyValue(33),
|
||||
memgraph::storage::PropertyValue(-33),
|
||||
memgraph::storage::PropertyValue(-3137),
|
||||
memgraph::storage::PropertyValue(3137),
|
||||
memgraph::storage::PropertyValue(310000007),
|
||||
memgraph::storage::PropertyValue(-310000007),
|
||||
memgraph::storage::PropertyValue(3100000000007L),
|
||||
memgraph::storage::PropertyValue(-3100000000007L),
|
||||
memgraph::storage::PropertyValue(0.0),
|
||||
memgraph::storage::PropertyValue(33.33),
|
||||
memgraph::storage::PropertyValue(-33.33),
|
||||
memgraph::storage::PropertyValue(3137.3137),
|
||||
memgraph::storage::PropertyValue(-3137.3137),
|
||||
memgraph::storage::PropertyValue("sample"),
|
||||
memgraph::storage::PropertyValue(std::string(404, 'n')),
|
||||
memgraph::storage::PropertyValue(std::vector<memgraph::storage::PropertyValue>{
|
||||
memgraph::storage::PropertyValue(33), memgraph::storage::PropertyValue(std::string("sample")),
|
||||
memgraph::storage::PropertyValue(-33.33)}),
|
||||
memgraph::storage::PropertyValue(std::vector<memgraph::storage::PropertyValue>{
|
||||
memgraph::storage::PropertyValue(), memgraph::storage::PropertyValue(false)}),
|
||||
memgraph::storage::PropertyValue(std::map<std::string, memgraph::storage::PropertyValue>{
|
||||
{"sample", memgraph::storage::PropertyValue()}, {"key", memgraph::storage::PropertyValue(false)}}),
|
||||
memgraph::storage::PropertyValue(std::map<std::string, memgraph::storage::PropertyValue>{
|
||||
{"test", memgraph::storage::PropertyValue(33)},
|
||||
{"map", memgraph::storage::PropertyValue(std::string("sample"))},
|
||||
{"item", memgraph::storage::PropertyValue(-33.33)}}),
|
||||
memgraph::storage::PropertyValue(memgraph::storage::TemporalData(memgraph::storage::TemporalType::Date, 23)),
|
||||
};
|
||||
|
||||
void TestIsPropertyEqual(const memgraph::storage::PropertyStore &store, memgraph::storage::PropertyId property,
|
||||
const memgraph::storage::PropertyValue &value) {
|
||||
ASSERT_TRUE(store.IsPropertyEqual(property, value));
|
||||
for (const auto &sample : kSampleValues) {
|
||||
if (sample == value) {
|
||||
ASSERT_TRUE(store.IsPropertyEqual(property, sample));
|
||||
} else {
|
||||
ASSERT_FALSE(store.IsPropertyEqual(property, sample));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(PropertyStore, Simple) {
|
||||
memgraph::storage::PropertyStore props;
|
||||
auto prop = memgraph::storage::PropertyId::FromInt(42);
|
||||
auto value = memgraph::storage::PropertyValue(42);
|
||||
ASSERT_TRUE(props.SetProperty(prop, value));
|
||||
ASSERT_EQ(props.GetProperty(prop), value);
|
||||
ASSERT_TRUE(props.HasProperty(prop));
|
||||
TestIsPropertyEqual(props, prop, value);
|
||||
ASSERT_THAT(props.Properties(), UnorderedElementsAre(std::pair(prop, value)));
|
||||
|
||||
ASSERT_FALSE(props.SetProperty(prop, memgraph::storage::PropertyValue()));
|
||||
ASSERT_TRUE(props.GetProperty(prop).IsNull());
|
||||
ASSERT_FALSE(props.HasProperty(prop));
|
||||
TestIsPropertyEqual(props, prop, memgraph::storage::PropertyValue());
|
||||
ASSERT_EQ(props.Properties().size(), 0);
|
||||
}
|
||||
|
||||
TEST(PropertyStore, SimpleLarge) {
|
||||
memgraph::storage::PropertyStore props;
|
||||
auto prop = memgraph::storage::PropertyId::FromInt(42);
|
||||
{
|
||||
auto value = memgraph::storage::PropertyValue(std::string(10000, 'a'));
|
||||
ASSERT_TRUE(props.SetProperty(prop, value));
|
||||
ASSERT_EQ(props.GetProperty(prop), value);
|
||||
ASSERT_TRUE(props.HasProperty(prop));
|
||||
TestIsPropertyEqual(props, prop, value);
|
||||
ASSERT_THAT(props.Properties(), UnorderedElementsAre(std::pair(prop, value)));
|
||||
}
|
||||
{
|
||||
auto value =
|
||||
memgraph::storage::PropertyValue(memgraph::storage::TemporalData(memgraph::storage::TemporalType::Date, 23));
|
||||
ASSERT_FALSE(props.SetProperty(prop, value));
|
||||
ASSERT_EQ(props.GetProperty(prop), value);
|
||||
ASSERT_TRUE(props.HasProperty(prop));
|
||||
TestIsPropertyEqual(props, prop, value);
|
||||
ASSERT_THAT(props.Properties(), UnorderedElementsAre(std::pair(prop, value)));
|
||||
}
|
||||
|
||||
ASSERT_FALSE(props.SetProperty(prop, memgraph::storage::PropertyValue()));
|
||||
ASSERT_TRUE(props.GetProperty(prop).IsNull());
|
||||
ASSERT_FALSE(props.HasProperty(prop));
|
||||
TestIsPropertyEqual(props, prop, memgraph::storage::PropertyValue());
|
||||
ASSERT_EQ(props.Properties().size(), 0);
|
||||
}
|
||||
|
||||
TEST(PropertyStore, EmptySetToNull) {
|
||||
memgraph::storage::PropertyStore props;
|
||||
auto prop = memgraph::storage::PropertyId::FromInt(42);
|
||||
ASSERT_TRUE(props.SetProperty(prop, memgraph::storage::PropertyValue()));
|
||||
ASSERT_TRUE(props.GetProperty(prop).IsNull());
|
||||
ASSERT_FALSE(props.HasProperty(prop));
|
||||
TestIsPropertyEqual(props, prop, memgraph::storage::PropertyValue());
|
||||
ASSERT_EQ(props.Properties().size(), 0);
|
||||
}
|
||||
|
||||
TEST(PropertyStore, Clear) {
|
||||
memgraph::storage::PropertyStore props;
|
||||
auto prop = memgraph::storage::PropertyId::FromInt(42);
|
||||
auto value = memgraph::storage::PropertyValue(42);
|
||||
ASSERT_TRUE(props.SetProperty(prop, value));
|
||||
ASSERT_EQ(props.GetProperty(prop), value);
|
||||
ASSERT_TRUE(props.HasProperty(prop));
|
||||
TestIsPropertyEqual(props, prop, value);
|
||||
ASSERT_THAT(props.Properties(), UnorderedElementsAre(std::pair(prop, value)));
|
||||
ASSERT_TRUE(props.ClearProperties());
|
||||
ASSERT_TRUE(props.GetProperty(prop).IsNull());
|
||||
ASSERT_FALSE(props.HasProperty(prop));
|
||||
TestIsPropertyEqual(props, prop, memgraph::storage::PropertyValue());
|
||||
ASSERT_EQ(props.Properties().size(), 0);
|
||||
}
|
||||
|
||||
TEST(PropertyStore, EmptyClear) {
|
||||
memgraph::storage::PropertyStore props;
|
||||
ASSERT_FALSE(props.ClearProperties());
|
||||
ASSERT_EQ(props.Properties().size(), 0);
|
||||
}
|
||||
|
||||
TEST(PropertyStore, MoveConstruct) {
|
||||
memgraph::storage::PropertyStore props1;
|
||||
auto prop = memgraph::storage::PropertyId::FromInt(42);
|
||||
auto value = memgraph::storage::PropertyValue(42);
|
||||
ASSERT_TRUE(props1.SetProperty(prop, value));
|
||||
ASSERT_EQ(props1.GetProperty(prop), value);
|
||||
ASSERT_TRUE(props1.HasProperty(prop));
|
||||
TestIsPropertyEqual(props1, prop, value);
|
||||
ASSERT_THAT(props1.Properties(), UnorderedElementsAre(std::pair(prop, value)));
|
||||
{
|
||||
memgraph::storage::PropertyStore props2(std::move(props1));
|
||||
ASSERT_EQ(props2.GetProperty(prop), value);
|
||||
ASSERT_TRUE(props2.HasProperty(prop));
|
||||
TestIsPropertyEqual(props2, prop, value);
|
||||
ASSERT_THAT(props2.Properties(), UnorderedElementsAre(std::pair(prop, value)));
|
||||
}
|
||||
// NOLINTNEXTLINE(bugprone-use-after-move,clang-analyzer-cplusplus.Move,hicpp-invalid-access-moved)
|
||||
ASSERT_TRUE(props1.GetProperty(prop).IsNull());
|
||||
ASSERT_FALSE(props1.HasProperty(prop));
|
||||
TestIsPropertyEqual(props1, prop, memgraph::storage::PropertyValue());
|
||||
ASSERT_EQ(props1.Properties().size(), 0);
|
||||
}
|
||||
|
||||
TEST(PropertyStore, MoveConstructLarge) {
|
||||
memgraph::storage::PropertyStore props1;
|
||||
auto prop = memgraph::storage::PropertyId::FromInt(42);
|
||||
auto value = memgraph::storage::PropertyValue(std::string(10000, 'a'));
|
||||
ASSERT_TRUE(props1.SetProperty(prop, value));
|
||||
ASSERT_EQ(props1.GetProperty(prop), value);
|
||||
ASSERT_TRUE(props1.HasProperty(prop));
|
||||
TestIsPropertyEqual(props1, prop, value);
|
||||
ASSERT_THAT(props1.Properties(), UnorderedElementsAre(std::pair(prop, value)));
|
||||
{
|
||||
memgraph::storage::PropertyStore props2(std::move(props1));
|
||||
ASSERT_EQ(props2.GetProperty(prop), value);
|
||||
ASSERT_TRUE(props2.HasProperty(prop));
|
||||
TestIsPropertyEqual(props2, prop, value);
|
||||
ASSERT_THAT(props2.Properties(), UnorderedElementsAre(std::pair(prop, value)));
|
||||
}
|
||||
// NOLINTNEXTLINE(bugprone-use-after-move,clang-analyzer-cplusplus.Move,hicpp-invalid-access-moved)
|
||||
ASSERT_TRUE(props1.GetProperty(prop).IsNull());
|
||||
ASSERT_FALSE(props1.HasProperty(prop));
|
||||
TestIsPropertyEqual(props1, prop, memgraph::storage::PropertyValue());
|
||||
ASSERT_EQ(props1.Properties().size(), 0);
|
||||
}
|
||||
|
||||
TEST(PropertyStore, MoveAssign) {
|
||||
memgraph::storage::PropertyStore props1;
|
||||
auto prop = memgraph::storage::PropertyId::FromInt(42);
|
||||
auto value = memgraph::storage::PropertyValue(42);
|
||||
ASSERT_TRUE(props1.SetProperty(prop, value));
|
||||
ASSERT_EQ(props1.GetProperty(prop), value);
|
||||
ASSERT_TRUE(props1.HasProperty(prop));
|
||||
TestIsPropertyEqual(props1, prop, value);
|
||||
ASSERT_THAT(props1.Properties(), UnorderedElementsAre(std::pair(prop, value)));
|
||||
{
|
||||
auto value2 = memgraph::storage::PropertyValue(68);
|
||||
memgraph::storage::PropertyStore props2;
|
||||
ASSERT_TRUE(props2.SetProperty(prop, value2));
|
||||
ASSERT_EQ(props2.GetProperty(prop), value2);
|
||||
ASSERT_TRUE(props2.HasProperty(prop));
|
||||
TestIsPropertyEqual(props2, prop, value2);
|
||||
ASSERT_THAT(props2.Properties(), UnorderedElementsAre(std::pair(prop, value2)));
|
||||
props2 = std::move(props1);
|
||||
ASSERT_EQ(props2.GetProperty(prop), value);
|
||||
ASSERT_TRUE(props2.HasProperty(prop));
|
||||
TestIsPropertyEqual(props2, prop, value);
|
||||
ASSERT_THAT(props2.Properties(), UnorderedElementsAre(std::pair(prop, value)));
|
||||
}
|
||||
// NOLINTNEXTLINE(bugprone-use-after-move,clang-analyzer-cplusplus.Move,hicpp-invalid-access-moved)
|
||||
ASSERT_TRUE(props1.GetProperty(prop).IsNull());
|
||||
ASSERT_FALSE(props1.HasProperty(prop));
|
||||
TestIsPropertyEqual(props1, prop, memgraph::storage::PropertyValue());
|
||||
ASSERT_EQ(props1.Properties().size(), 0);
|
||||
}
|
||||
|
||||
TEST(PropertyStore, MoveAssignLarge) {
|
||||
memgraph::storage::PropertyStore props1;
|
||||
auto prop = memgraph::storage::PropertyId::FromInt(42);
|
||||
auto value = memgraph::storage::PropertyValue(std::string(10000, 'a'));
|
||||
ASSERT_TRUE(props1.SetProperty(prop, value));
|
||||
ASSERT_EQ(props1.GetProperty(prop), value);
|
||||
ASSERT_TRUE(props1.HasProperty(prop));
|
||||
TestIsPropertyEqual(props1, prop, value);
|
||||
ASSERT_THAT(props1.Properties(), UnorderedElementsAre(std::pair(prop, value)));
|
||||
{
|
||||
auto value2 = memgraph::storage::PropertyValue(std::string(10000, 'b'));
|
||||
memgraph::storage::PropertyStore props2;
|
||||
ASSERT_TRUE(props2.SetProperty(prop, value2));
|
||||
ASSERT_EQ(props2.GetProperty(prop), value2);
|
||||
ASSERT_TRUE(props2.HasProperty(prop));
|
||||
TestIsPropertyEqual(props2, prop, value2);
|
||||
ASSERT_THAT(props2.Properties(), UnorderedElementsAre(std::pair(prop, value2)));
|
||||
props2 = std::move(props1);
|
||||
ASSERT_EQ(props2.GetProperty(prop), value);
|
||||
ASSERT_TRUE(props2.HasProperty(prop));
|
||||
TestIsPropertyEqual(props2, prop, value);
|
||||
ASSERT_THAT(props2.Properties(), UnorderedElementsAre(std::pair(prop, value)));
|
||||
}
|
||||
// NOLINTNEXTLINE(bugprone-use-after-move,clang-analyzer-cplusplus.Move,hicpp-invalid-access-moved)
|
||||
ASSERT_TRUE(props1.GetProperty(prop).IsNull());
|
||||
ASSERT_FALSE(props1.HasProperty(prop));
|
||||
TestIsPropertyEqual(props1, prop, memgraph::storage::PropertyValue());
|
||||
ASSERT_EQ(props1.Properties().size(), 0);
|
||||
}
|
||||
|
||||
TEST(PropertyStore, EmptySet) {
|
||||
std::vector<memgraph::storage::PropertyValue> vec{memgraph::storage::PropertyValue(true),
|
||||
memgraph::storage::PropertyValue(123),
|
||||
memgraph::storage::PropertyValue()};
|
||||
std::map<std::string, memgraph::storage::PropertyValue> map{{"nandare", memgraph::storage::PropertyValue(false)}};
|
||||
const memgraph::storage::TemporalData temporal{memgraph::storage::TemporalType::LocalDateTime, 23};
|
||||
std::vector<memgraph::storage::PropertyValue> data{
|
||||
memgraph::storage::PropertyValue(true), memgraph::storage::PropertyValue(123),
|
||||
memgraph::storage::PropertyValue(123.5), memgraph::storage::PropertyValue("nandare"),
|
||||
memgraph::storage::PropertyValue(vec), memgraph::storage::PropertyValue(map),
|
||||
memgraph::storage::PropertyValue(temporal)};
|
||||
|
||||
auto prop = memgraph::storage::PropertyId::FromInt(42);
|
||||
for (const auto &value : data) {
|
||||
memgraph::storage::PropertyStore props;
|
||||
|
||||
ASSERT_TRUE(props.SetProperty(prop, value));
|
||||
ASSERT_EQ(props.GetProperty(prop), value);
|
||||
ASSERT_TRUE(props.HasProperty(prop));
|
||||
TestIsPropertyEqual(props, prop, value);
|
||||
ASSERT_THAT(props.Properties(), UnorderedElementsAre(std::pair(prop, value)));
|
||||
ASSERT_FALSE(props.SetProperty(prop, value));
|
||||
ASSERT_EQ(props.GetProperty(prop), value);
|
||||
ASSERT_TRUE(props.HasProperty(prop));
|
||||
TestIsPropertyEqual(props, prop, value);
|
||||
ASSERT_THAT(props.Properties(), UnorderedElementsAre(std::pair(prop, value)));
|
||||
ASSERT_FALSE(props.SetProperty(prop, memgraph::storage::PropertyValue()));
|
||||
ASSERT_TRUE(props.GetProperty(prop).IsNull());
|
||||
ASSERT_FALSE(props.HasProperty(prop));
|
||||
TestIsPropertyEqual(props, prop, memgraph::storage::PropertyValue());
|
||||
ASSERT_EQ(props.Properties().size(), 0);
|
||||
ASSERT_TRUE(props.SetProperty(prop, memgraph::storage::PropertyValue()));
|
||||
ASSERT_TRUE(props.GetProperty(prop).IsNull());
|
||||
ASSERT_FALSE(props.HasProperty(prop));
|
||||
TestIsPropertyEqual(props, prop, memgraph::storage::PropertyValue());
|
||||
ASSERT_EQ(props.Properties().size(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(PropertyStore, FullSet) {
|
||||
std::vector<memgraph::storage::PropertyValue> vec{memgraph::storage::PropertyValue(true),
|
||||
memgraph::storage::PropertyValue(123),
|
||||
memgraph::storage::PropertyValue()};
|
||||
std::map<std::string, memgraph::storage::PropertyValue> map{{"nandare", memgraph::storage::PropertyValue(false)}};
|
||||
const memgraph::storage::TemporalData temporal{memgraph::storage::TemporalType::LocalDateTime, 23};
|
||||
std::map<memgraph::storage::PropertyId, memgraph::storage::PropertyValue> data{
|
||||
{memgraph::storage::PropertyId::FromInt(1), memgraph::storage::PropertyValue(true)},
|
||||
{memgraph::storage::PropertyId::FromInt(2), memgraph::storage::PropertyValue(123)},
|
||||
{memgraph::storage::PropertyId::FromInt(3), memgraph::storage::PropertyValue(123.5)},
|
||||
{memgraph::storage::PropertyId::FromInt(4), memgraph::storage::PropertyValue("nandare")},
|
||||
{memgraph::storage::PropertyId::FromInt(5), memgraph::storage::PropertyValue(vec)},
|
||||
{memgraph::storage::PropertyId::FromInt(6), memgraph::storage::PropertyValue(map)},
|
||||
{memgraph::storage::PropertyId::FromInt(7), memgraph::storage::PropertyValue(temporal)}};
|
||||
|
||||
std::vector<memgraph::storage::PropertyValue> alt{memgraph::storage::PropertyValue(),
|
||||
memgraph::storage::PropertyValue(std::string()),
|
||||
memgraph::storage::PropertyValue(std::string(10, 'a')),
|
||||
memgraph::storage::PropertyValue(std::string(100, 'a')),
|
||||
memgraph::storage::PropertyValue(std::string(1000, 'a')),
|
||||
memgraph::storage::PropertyValue(std::string(10000, 'a')),
|
||||
memgraph::storage::PropertyValue(std::string(100000, 'a'))};
|
||||
|
||||
memgraph::storage::PropertyStore props;
|
||||
for (const auto &target : data) {
|
||||
for (const auto &item : data) {
|
||||
ASSERT_TRUE(props.SetProperty(item.first, item.second));
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < alt.size(); ++i) {
|
||||
if (i == 1) {
|
||||
ASSERT_TRUE(props.SetProperty(target.first, alt[i]));
|
||||
} else {
|
||||
ASSERT_FALSE(props.SetProperty(target.first, alt[i]));
|
||||
}
|
||||
for (const auto &item : data) {
|
||||
if (item.first == target.first) {
|
||||
ASSERT_EQ(props.GetProperty(item.first), alt[i]);
|
||||
if (alt[i].IsNull()) {
|
||||
ASSERT_FALSE(props.HasProperty(item.first));
|
||||
} else {
|
||||
ASSERT_TRUE(props.HasProperty(item.first));
|
||||
}
|
||||
TestIsPropertyEqual(props, item.first, alt[i]);
|
||||
} else {
|
||||
ASSERT_EQ(props.GetProperty(item.first), item.second);
|
||||
ASSERT_TRUE(props.HasProperty(item.first));
|
||||
TestIsPropertyEqual(props, item.first, item.second);
|
||||
}
|
||||
}
|
||||
auto current = data;
|
||||
if (alt[i].IsNull()) {
|
||||
current.erase(target.first);
|
||||
} else {
|
||||
current[target.first] = alt[i];
|
||||
}
|
||||
ASSERT_EQ(props.Properties(), current);
|
||||
}
|
||||
|
||||
for (ssize_t i = alt.size() - 1; i >= 0; --i) {
|
||||
ASSERT_FALSE(props.SetProperty(target.first, alt[i]));
|
||||
for (const auto &item : data) {
|
||||
if (item.first == target.first) {
|
||||
ASSERT_EQ(props.GetProperty(item.first), alt[i]);
|
||||
if (alt[i].IsNull()) {
|
||||
ASSERT_FALSE(props.HasProperty(item.first));
|
||||
} else {
|
||||
ASSERT_TRUE(props.HasProperty(item.first));
|
||||
}
|
||||
TestIsPropertyEqual(props, item.first, alt[i]);
|
||||
} else {
|
||||
ASSERT_EQ(props.GetProperty(item.first), item.second);
|
||||
ASSERT_TRUE(props.HasProperty(item.first));
|
||||
TestIsPropertyEqual(props, item.first, item.second);
|
||||
}
|
||||
}
|
||||
auto current = data;
|
||||
if (alt[i].IsNull()) {
|
||||
current.erase(target.first);
|
||||
} else {
|
||||
current[target.first] = alt[i];
|
||||
}
|
||||
ASSERT_EQ(props.Properties(), current);
|
||||
}
|
||||
|
||||
ASSERT_TRUE(props.SetProperty(target.first, target.second));
|
||||
ASSERT_EQ(props.GetProperty(target.first), target.second);
|
||||
ASSERT_TRUE(props.HasProperty(target.first));
|
||||
TestIsPropertyEqual(props, target.first, target.second);
|
||||
|
||||
props.ClearProperties();
|
||||
ASSERT_EQ(props.Properties().size(), 0);
|
||||
for (const auto &item : data) {
|
||||
ASSERT_TRUE(props.GetProperty(item.first).IsNull());
|
||||
ASSERT_FALSE(props.HasProperty(item.first));
|
||||
TestIsPropertyEqual(props, item.first, memgraph::storage::PropertyValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(PropertyStore, IntEncoding) {
|
||||
std::map<memgraph::storage::PropertyId, memgraph::storage::PropertyValue> data{
|
||||
{memgraph::storage::PropertyId::FromUint(0UL),
|
||||
memgraph::storage::PropertyValue(std::numeric_limits<int64_t>::min())},
|
||||
{memgraph::storage::PropertyId::FromUint(10UL), memgraph::storage::PropertyValue(-137438953472L)},
|
||||
{memgraph::storage::PropertyId::FromUint(std::numeric_limits<uint8_t>::max()),
|
||||
memgraph::storage::PropertyValue(-4294967297L)},
|
||||
{memgraph::storage::PropertyId::FromUint(256UL),
|
||||
memgraph::storage::PropertyValue(std::numeric_limits<int32_t>::min())},
|
||||
{memgraph::storage::PropertyId::FromUint(1024UL), memgraph::storage::PropertyValue(-1048576L)},
|
||||
{memgraph::storage::PropertyId::FromUint(1025UL), memgraph::storage::PropertyValue(-65537L)},
|
||||
{memgraph::storage::PropertyId::FromUint(1026UL),
|
||||
memgraph::storage::PropertyValue(std::numeric_limits<int16_t>::min())},
|
||||
{memgraph::storage::PropertyId::FromUint(1027UL), memgraph::storage::PropertyValue(-1024L)},
|
||||
{memgraph::storage::PropertyId::FromUint(2000UL), memgraph::storage::PropertyValue(-257L)},
|
||||
{memgraph::storage::PropertyId::FromUint(3000UL),
|
||||
memgraph::storage::PropertyValue(std::numeric_limits<int8_t>::min())},
|
||||
{memgraph::storage::PropertyId::FromUint(4000UL), memgraph::storage::PropertyValue(-1L)},
|
||||
{memgraph::storage::PropertyId::FromUint(10000UL), memgraph::storage::PropertyValue(0L)},
|
||||
{memgraph::storage::PropertyId::FromUint(20000UL), memgraph::storage::PropertyValue(1L)},
|
||||
{memgraph::storage::PropertyId::FromUint(30000UL),
|
||||
memgraph::storage::PropertyValue(std::numeric_limits<int8_t>::max())},
|
||||
{memgraph::storage::PropertyId::FromUint(40000UL), memgraph::storage::PropertyValue(256L)},
|
||||
{memgraph::storage::PropertyId::FromUint(50000UL), memgraph::storage::PropertyValue(1024L)},
|
||||
{memgraph::storage::PropertyId::FromUint(std::numeric_limits<uint16_t>::max()),
|
||||
memgraph::storage::PropertyValue(std::numeric_limits<int16_t>::max())},
|
||||
{memgraph::storage::PropertyId::FromUint(65536UL), memgraph::storage::PropertyValue(65536L)},
|
||||
{memgraph::storage::PropertyId::FromUint(1048576UL), memgraph::storage::PropertyValue(1048576L)},
|
||||
{memgraph::storage::PropertyId::FromUint(std::numeric_limits<uint32_t>::max()),
|
||||
memgraph::storage::PropertyValue(std::numeric_limits<int32_t>::max())},
|
||||
{memgraph::storage::PropertyId::FromUint(4294967296UL), memgraph::storage::PropertyValue(4294967296L)},
|
||||
{memgraph::storage::PropertyId::FromUint(137438953472UL), memgraph::storage::PropertyValue(137438953472L)},
|
||||
{memgraph::storage::PropertyId::FromUint(std::numeric_limits<uint64_t>::max()),
|
||||
memgraph::storage::PropertyValue(std::numeric_limits<int64_t>::max())}};
|
||||
|
||||
memgraph::storage::PropertyStore props;
|
||||
for (const auto &item : data) {
|
||||
ASSERT_TRUE(props.SetProperty(item.first, item.second));
|
||||
ASSERT_EQ(props.GetProperty(item.first), item.second);
|
||||
ASSERT_TRUE(props.HasProperty(item.first));
|
||||
TestIsPropertyEqual(props, item.first, item.second);
|
||||
}
|
||||
for (auto it = data.rbegin(); it != data.rend(); ++it) {
|
||||
const auto &item = *it;
|
||||
ASSERT_FALSE(props.SetProperty(item.first, item.second));
|
||||
ASSERT_EQ(props.GetProperty(item.first), item.second);
|
||||
ASSERT_TRUE(props.HasProperty(item.first));
|
||||
TestIsPropertyEqual(props, item.first, item.second);
|
||||
}
|
||||
|
||||
ASSERT_EQ(props.Properties(), data);
|
||||
|
||||
props.ClearProperties();
|
||||
ASSERT_EQ(props.Properties().size(), 0);
|
||||
for (const auto &item : data) {
|
||||
ASSERT_TRUE(props.GetProperty(item.first).IsNull());
|
||||
ASSERT_FALSE(props.HasProperty(item.first));
|
||||
TestIsPropertyEqual(props, item.first, memgraph::storage::PropertyValue());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(PropertyStore, IsPropertyEqualIntAndDouble) {
|
||||
memgraph::storage::PropertyStore props;
|
||||
auto prop = memgraph::storage::PropertyId::FromInt(42);
|
||||
|
||||
ASSERT_TRUE(props.SetProperty(prop, memgraph::storage::PropertyValue(42)));
|
||||
|
||||
std::vector<std::pair<memgraph::storage::PropertyValue, memgraph::storage::PropertyValue>> tests{
|
||||
{memgraph::storage::PropertyValue(0), memgraph::storage::PropertyValue(0.0)},
|
||||
{memgraph::storage::PropertyValue(123), memgraph::storage::PropertyValue(123.0)},
|
||||
{memgraph::storage::PropertyValue(12345), memgraph::storage::PropertyValue(12345.0)},
|
||||
{memgraph::storage::PropertyValue(12345678), memgraph::storage::PropertyValue(12345678.0)},
|
||||
{memgraph::storage::PropertyValue(1234567890123L), memgraph::storage::PropertyValue(1234567890123.0)},
|
||||
};
|
||||
|
||||
// Test equality with raw values.
|
||||
for (auto test : tests) {
|
||||
ASSERT_EQ(test.first, test.second);
|
||||
|
||||
// Test first, second
|
||||
ASSERT_FALSE(props.SetProperty(prop, test.first));
|
||||
ASSERT_EQ(props.GetProperty(prop), test.first);
|
||||
ASSERT_TRUE(props.HasProperty(prop));
|
||||
ASSERT_TRUE(props.IsPropertyEqual(prop, test.first));
|
||||
ASSERT_TRUE(props.IsPropertyEqual(prop, test.second));
|
||||
|
||||
// Test second, first
|
||||
ASSERT_FALSE(props.SetProperty(prop, test.second));
|
||||
ASSERT_EQ(props.GetProperty(prop), test.second);
|
||||
ASSERT_TRUE(props.HasProperty(prop));
|
||||
ASSERT_TRUE(props.IsPropertyEqual(prop, test.second));
|
||||
ASSERT_TRUE(props.IsPropertyEqual(prop, test.first));
|
||||
|
||||
// Make both negative
|
||||
test.first = memgraph::storage::PropertyValue(test.first.ValueInt() * -1);
|
||||
test.second = memgraph::storage::PropertyValue(test.second.ValueDouble() * -1.0);
|
||||
ASSERT_EQ(test.first, test.second);
|
||||
|
||||
// Test -first, -second
|
||||
ASSERT_FALSE(props.SetProperty(prop, test.first));
|
||||
ASSERT_EQ(props.GetProperty(prop), test.first);
|
||||
ASSERT_TRUE(props.HasProperty(prop));
|
||||
ASSERT_TRUE(props.IsPropertyEqual(prop, test.first));
|
||||
ASSERT_TRUE(props.IsPropertyEqual(prop, test.second));
|
||||
|
||||
// Test -second, -first
|
||||
ASSERT_FALSE(props.SetProperty(prop, test.second));
|
||||
ASSERT_EQ(props.GetProperty(prop), test.second);
|
||||
ASSERT_TRUE(props.HasProperty(prop));
|
||||
ASSERT_TRUE(props.IsPropertyEqual(prop, test.second));
|
||||
ASSERT_TRUE(props.IsPropertyEqual(prop, test.first));
|
||||
}
|
||||
|
||||
// Test equality with values wrapped in lists.
|
||||
for (auto test : tests) {
|
||||
test.first = memgraph::storage::PropertyValue(
|
||||
std::vector<memgraph::storage::PropertyValue>{memgraph::storage::PropertyValue(test.first.ValueInt())});
|
||||
test.second = memgraph::storage::PropertyValue(
|
||||
std::vector<memgraph::storage::PropertyValue>{memgraph::storage::PropertyValue(test.second.ValueDouble())});
|
||||
ASSERT_EQ(test.first, test.second);
|
||||
|
||||
// Test first, second
|
||||
ASSERT_FALSE(props.SetProperty(prop, test.first));
|
||||
ASSERT_EQ(props.GetProperty(prop), test.first);
|
||||
ASSERT_TRUE(props.HasProperty(prop));
|
||||
ASSERT_TRUE(props.IsPropertyEqual(prop, test.first));
|
||||
ASSERT_TRUE(props.IsPropertyEqual(prop, test.second));
|
||||
|
||||
// Test second, first
|
||||
ASSERT_FALSE(props.SetProperty(prop, test.second));
|
||||
ASSERT_EQ(props.GetProperty(prop), test.second);
|
||||
ASSERT_TRUE(props.HasProperty(prop));
|
||||
ASSERT_TRUE(props.IsPropertyEqual(prop, test.second));
|
||||
ASSERT_TRUE(props.IsPropertyEqual(prop, test.first));
|
||||
|
||||
// Make both negative
|
||||
test.first = memgraph::storage::PropertyValue(std::vector<memgraph::storage::PropertyValue>{
|
||||
memgraph::storage::PropertyValue(test.first.ValueList()[0].ValueInt() * -1)});
|
||||
test.second = memgraph::storage::PropertyValue(std::vector<memgraph::storage::PropertyValue>{
|
||||
memgraph::storage::PropertyValue(test.second.ValueList()[0].ValueDouble() * -1.0)});
|
||||
ASSERT_EQ(test.first, test.second);
|
||||
|
||||
// Test -first, -second
|
||||
ASSERT_FALSE(props.SetProperty(prop, test.first));
|
||||
ASSERT_EQ(props.GetProperty(prop), test.first);
|
||||
ASSERT_TRUE(props.HasProperty(prop));
|
||||
ASSERT_TRUE(props.IsPropertyEqual(prop, test.first));
|
||||
ASSERT_TRUE(props.IsPropertyEqual(prop, test.second));
|
||||
|
||||
// Test -second, -first
|
||||
ASSERT_FALSE(props.SetProperty(prop, test.second));
|
||||
ASSERT_EQ(props.GetProperty(prop), test.second);
|
||||
ASSERT_TRUE(props.HasProperty(prop));
|
||||
ASSERT_TRUE(props.IsPropertyEqual(prop, test.second));
|
||||
ASSERT_TRUE(props.IsPropertyEqual(prop, test.first));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(PropertyStore, IsPropertyEqualString) {
|
||||
memgraph::storage::PropertyStore props;
|
||||
auto prop = memgraph::storage::PropertyId::FromInt(42);
|
||||
ASSERT_TRUE(props.SetProperty(prop, memgraph::storage::PropertyValue("test")));
|
||||
ASSERT_TRUE(props.IsPropertyEqual(prop, memgraph::storage::PropertyValue("test")));
|
||||
|
||||
// Different length.
|
||||
ASSERT_FALSE(props.IsPropertyEqual(prop, memgraph::storage::PropertyValue("helloworld")));
|
||||
|
||||
// Same length, different value.
|
||||
ASSERT_FALSE(props.IsPropertyEqual(prop, memgraph::storage::PropertyValue("asdf")));
|
||||
|
||||
// Shortened and extended.
|
||||
ASSERT_FALSE(props.IsPropertyEqual(prop, memgraph::storage::PropertyValue("tes")));
|
||||
ASSERT_FALSE(props.IsPropertyEqual(prop, memgraph::storage::PropertyValue("testt")));
|
||||
}
|
||||
|
||||
TEST(PropertyStore, IsPropertyEqualList) {
|
||||
memgraph::storage::PropertyStore props;
|
||||
auto prop = memgraph::storage::PropertyId::FromInt(42);
|
||||
ASSERT_TRUE(
|
||||
props.SetProperty(prop, memgraph::storage::PropertyValue(std::vector<memgraph::storage::PropertyValue>{
|
||||
memgraph::storage::PropertyValue(42), memgraph::storage::PropertyValue("test")})));
|
||||
ASSERT_TRUE(props.IsPropertyEqual(
|
||||
prop, memgraph::storage::PropertyValue(std::vector<memgraph::storage::PropertyValue>{
|
||||
memgraph::storage::PropertyValue(42), memgraph::storage::PropertyValue("test")})));
|
||||
|
||||
// Different length.
|
||||
ASSERT_FALSE(props.IsPropertyEqual(
|
||||
prop, memgraph::storage::PropertyValue(
|
||||
std::vector<memgraph::storage::PropertyValue>{memgraph::storage::PropertyValue(24)})));
|
||||
|
||||
// Same length, different value.
|
||||
ASSERT_FALSE(props.IsPropertyEqual(
|
||||
prop, memgraph::storage::PropertyValue(std::vector<memgraph::storage::PropertyValue>{
|
||||
memgraph::storage::PropertyValue(42), memgraph::storage::PropertyValue("asdf")})));
|
||||
|
||||
// Shortened and extended.
|
||||
ASSERT_FALSE(props.IsPropertyEqual(
|
||||
prop, memgraph::storage::PropertyValue(
|
||||
std::vector<memgraph::storage::PropertyValue>{memgraph::storage::PropertyValue(42)})));
|
||||
ASSERT_FALSE(
|
||||
props.IsPropertyEqual(prop, memgraph::storage::PropertyValue(std::vector<memgraph::storage::PropertyValue>{
|
||||
memgraph::storage::PropertyValue(42), memgraph::storage::PropertyValue("test"),
|
||||
memgraph::storage::PropertyValue(true)})));
|
||||
}
|
||||
|
||||
TEST(PropertyStore, IsPropertyEqualMap) {
|
||||
memgraph::storage::PropertyStore props;
|
||||
auto prop = memgraph::storage::PropertyId::FromInt(42);
|
||||
ASSERT_TRUE(props.SetProperty(
|
||||
prop, memgraph::storage::PropertyValue(std::map<std::string, memgraph::storage::PropertyValue>{
|
||||
{"abc", memgraph::storage::PropertyValue(42)}, {"zyx", memgraph::storage::PropertyValue("test")}})));
|
||||
ASSERT_TRUE(props.IsPropertyEqual(
|
||||
prop, memgraph::storage::PropertyValue(std::map<std::string, memgraph::storage::PropertyValue>{
|
||||
{"abc", memgraph::storage::PropertyValue(42)}, {"zyx", memgraph::storage::PropertyValue("test")}})));
|
||||
|
||||
// Different length.
|
||||
ASSERT_FALSE(props.IsPropertyEqual(
|
||||
prop, memgraph::storage::PropertyValue(std::map<std::string, memgraph::storage::PropertyValue>{
|
||||
{"fgh", memgraph::storage::PropertyValue(24)}})));
|
||||
|
||||
// Same length, different value.
|
||||
ASSERT_FALSE(props.IsPropertyEqual(
|
||||
prop, memgraph::storage::PropertyValue(std::map<std::string, memgraph::storage::PropertyValue>{
|
||||
{"abc", memgraph::storage::PropertyValue(42)}, {"zyx", memgraph::storage::PropertyValue("testt")}})));
|
||||
|
||||
// Same length, different key (different length).
|
||||
ASSERT_FALSE(props.IsPropertyEqual(
|
||||
prop, memgraph::storage::PropertyValue(std::map<std::string, memgraph::storage::PropertyValue>{
|
||||
{"abc", memgraph::storage::PropertyValue(42)}, {"zyxw", memgraph::storage::PropertyValue("test")}})));
|
||||
|
||||
// Same length, different key (same length).
|
||||
ASSERT_FALSE(props.IsPropertyEqual(
|
||||
prop, memgraph::storage::PropertyValue(std::map<std::string, memgraph::storage::PropertyValue>{
|
||||
{"abc", memgraph::storage::PropertyValue(42)}, {"zyw", memgraph::storage::PropertyValue("test")}})));
|
||||
|
||||
// Shortened and extended.
|
||||
ASSERT_FALSE(props.IsPropertyEqual(
|
||||
prop, memgraph::storage::PropertyValue(std::map<std::string, memgraph::storage::PropertyValue>{
|
||||
{"abc", memgraph::storage::PropertyValue(42)}})));
|
||||
ASSERT_FALSE(props.IsPropertyEqual(
|
||||
prop, memgraph::storage::PropertyValue(std::map<std::string, memgraph::storage::PropertyValue>{
|
||||
{"abc", memgraph::storage::PropertyValue(42)},
|
||||
{"sdf", memgraph::storage::PropertyValue(true)},
|
||||
{"zyx", memgraph::storage::PropertyValue("test")}})));
|
||||
}
|
||||
|
||||
TEST(PropertyStore, IsPropertyEqualTemporalData) {
|
||||
memgraph::storage::PropertyStore props;
|
||||
auto prop = memgraph::storage::PropertyId::FromInt(42);
|
||||
const memgraph::storage::TemporalData temporal{memgraph::storage::TemporalType::Date, 23};
|
||||
ASSERT_TRUE(props.SetProperty(prop, memgraph::storage::PropertyValue(temporal)));
|
||||
ASSERT_TRUE(props.IsPropertyEqual(prop, memgraph::storage::PropertyValue(temporal)));
|
||||
|
||||
// Different type.
|
||||
ASSERT_FALSE(props.IsPropertyEqual(prop, memgraph::storage::PropertyValue(memgraph::storage::TemporalData{
|
||||
memgraph::storage::TemporalType::Duration, 23})));
|
||||
|
||||
// Same type, different value.
|
||||
ASSERT_FALSE(props.IsPropertyEqual(prop, memgraph::storage::PropertyValue(memgraph::storage::TemporalData{
|
||||
memgraph::storage::TemporalType::Date, 30})));
|
||||
}
|
@ -1,828 +0,0 @@
|
||||
// Copyright 2022 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 <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <gmock/gmock-generated-matchers.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <storage/v2/property_value.hpp>
|
||||
#include <storage/v2/replication/enums.hpp>
|
||||
#include <storage/v2/storage.hpp>
|
||||
#include "storage/v2/view.hpp"
|
||||
|
||||
using testing::UnorderedElementsAre;
|
||||
|
||||
class ReplicationTest : public ::testing::Test {
|
||||
protected:
|
||||
std::filesystem::path storage_directory{std::filesystem::temp_directory_path() /
|
||||
"MG_test_unit_storage_v2_replication"};
|
||||
void SetUp() override { Clear(); }
|
||||
|
||||
void TearDown() override { Clear(); }
|
||||
|
||||
memgraph::storage::Config configuration{
|
||||
.items = {.properties_on_edges = true},
|
||||
.durability = {
|
||||
.storage_directory = storage_directory,
|
||||
.snapshot_wal_mode = memgraph::storage::Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT_WITH_WAL,
|
||||
}};
|
||||
|
||||
const std::string local_host = ("127.0.0.1");
|
||||
const std::array<uint16_t, 2> ports{10000, 20000};
|
||||
const std::array<std::string, 2> replicas = {"REPLICA1", "REPLICA2"};
|
||||
|
||||
private:
|
||||
void Clear() {
|
||||
if (!std::filesystem::exists(storage_directory)) return;
|
||||
std::filesystem::remove_all(storage_directory);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(ReplicationTest, BasicSynchronousReplicationTest) {
|
||||
memgraph::storage::Storage main_store(configuration);
|
||||
|
||||
memgraph::storage::Storage replica_store(configuration);
|
||||
replica_store.SetReplicaRole(memgraph::io::network::Endpoint{local_host, ports[0]});
|
||||
|
||||
ASSERT_FALSE(main_store
|
||||
.RegisterReplica("REPLICA", memgraph::io::network::Endpoint{local_host, ports[0]},
|
||||
memgraph::storage::replication::ReplicationMode::SYNC,
|
||||
memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID)
|
||||
.HasError());
|
||||
|
||||
// vertex create
|
||||
// vertex add label
|
||||
// vertex set property
|
||||
const auto *vertex_label = "vertex_label";
|
||||
const auto *vertex_property = "vertex_property";
|
||||
const auto *vertex_property_value = "vertex_property_value";
|
||||
std::optional<memgraph::storage::Gid> vertex_gid;
|
||||
{
|
||||
auto acc = main_store.Access();
|
||||
auto v = acc.CreateVertex();
|
||||
vertex_gid.emplace(v.Gid());
|
||||
ASSERT_TRUE(v.AddLabel(main_store.NameToLabel(vertex_label)).HasValue());
|
||||
ASSERT_TRUE(v.SetProperty(main_store.NameToProperty(vertex_property),
|
||||
memgraph::storage::PropertyValue(vertex_property_value))
|
||||
.HasValue());
|
||||
ASSERT_FALSE(acc.Commit().HasError());
|
||||
}
|
||||
|
||||
{
|
||||
auto acc = replica_store.Access();
|
||||
const auto v = acc.FindVertex(*vertex_gid, memgraph::storage::View::OLD);
|
||||
ASSERT_TRUE(v);
|
||||
const auto labels = v->Labels(memgraph::storage::View::OLD);
|
||||
ASSERT_TRUE(labels.HasValue());
|
||||
ASSERT_EQ(labels->size(), 1);
|
||||
ASSERT_THAT(*labels, UnorderedElementsAre(replica_store.NameToLabel(vertex_label)));
|
||||
const auto properties = v->Properties(memgraph::storage::View::OLD);
|
||||
ASSERT_TRUE(properties.HasValue());
|
||||
ASSERT_EQ(properties->size(), 1);
|
||||
ASSERT_THAT(*properties,
|
||||
UnorderedElementsAre(std::make_pair(replica_store.NameToProperty(vertex_property),
|
||||
memgraph::storage::PropertyValue(vertex_property_value))));
|
||||
|
||||
ASSERT_FALSE(acc.Commit().HasError());
|
||||
}
|
||||
|
||||
// vertex remove label
|
||||
{
|
||||
auto acc = main_store.Access();
|
||||
auto v = acc.FindVertex(*vertex_gid, memgraph::storage::View::OLD);
|
||||
ASSERT_TRUE(v);
|
||||
ASSERT_TRUE(v->RemoveLabel(main_store.NameToLabel(vertex_label)).HasValue());
|
||||
ASSERT_FALSE(acc.Commit().HasError());
|
||||
}
|
||||
|
||||
{
|
||||
auto acc = replica_store.Access();
|
||||
const auto v = acc.FindVertex(*vertex_gid, memgraph::storage::View::OLD);
|
||||
ASSERT_TRUE(v);
|
||||
const auto labels = v->Labels(memgraph::storage::View::OLD);
|
||||
ASSERT_TRUE(labels.HasValue());
|
||||
ASSERT_EQ(labels->size(), 0);
|
||||
ASSERT_FALSE(acc.Commit().HasError());
|
||||
}
|
||||
|
||||
// vertex delete
|
||||
{
|
||||
auto acc = main_store.Access();
|
||||
auto v = acc.FindVertex(*vertex_gid, memgraph::storage::View::OLD);
|
||||
ASSERT_TRUE(v);
|
||||
ASSERT_TRUE(acc.DeleteVertex(&*v).HasValue());
|
||||
ASSERT_FALSE(acc.Commit().HasError());
|
||||
}
|
||||
|
||||
{
|
||||
auto acc = replica_store.Access();
|
||||
const auto v = acc.FindVertex(*vertex_gid, memgraph::storage::View::OLD);
|
||||
ASSERT_FALSE(v);
|
||||
vertex_gid.reset();
|
||||
ASSERT_FALSE(acc.Commit().HasError());
|
||||
}
|
||||
|
||||
// edge create
|
||||
// edge set property
|
||||
const auto *edge_type = "edge_type";
|
||||
const auto *edge_property = "edge_property";
|
||||
const auto *edge_property_value = "edge_property_value";
|
||||
std::optional<memgraph::storage::Gid> edge_gid;
|
||||
{
|
||||
auto acc = main_store.Access();
|
||||
auto v = acc.CreateVertex();
|
||||
vertex_gid.emplace(v.Gid());
|
||||
auto edge = acc.CreateEdge(&v, &v, main_store.NameToEdgeType(edge_type));
|
||||
ASSERT_TRUE(edge.HasValue());
|
||||
ASSERT_TRUE(edge->SetProperty(main_store.NameToProperty(edge_property),
|
||||
memgraph::storage::PropertyValue(edge_property_value))
|
||||
.HasValue());
|
||||
edge_gid.emplace(edge->Gid());
|
||||
ASSERT_FALSE(acc.Commit().HasError());
|
||||
}
|
||||
|
||||
const auto find_edge = [&](const auto &edges,
|
||||
const memgraph::storage::Gid edge_gid) -> std::optional<memgraph::storage::EdgeAccessor> {
|
||||
for (const auto &edge : edges) {
|
||||
if (edge.Gid() == edge_gid) {
|
||||
return edge;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
};
|
||||
|
||||
{
|
||||
auto acc = replica_store.Access();
|
||||
const auto v = acc.FindVertex(*vertex_gid, memgraph::storage::View::OLD);
|
||||
ASSERT_TRUE(v);
|
||||
const auto out_edges = v->OutEdges(memgraph::storage::View::OLD);
|
||||
ASSERT_TRUE(out_edges.HasValue());
|
||||
const auto edge = find_edge(*out_edges, *edge_gid);
|
||||
ASSERT_EQ(edge->EdgeType(), replica_store.NameToEdgeType(edge_type));
|
||||
const auto properties = edge->Properties(memgraph::storage::View::OLD);
|
||||
ASSERT_TRUE(properties.HasValue());
|
||||
ASSERT_EQ(properties->size(), 1);
|
||||
ASSERT_THAT(*properties,
|
||||
UnorderedElementsAre(std::make_pair(replica_store.NameToProperty(edge_property),
|
||||
memgraph::storage::PropertyValue(edge_property_value))));
|
||||
ASSERT_FALSE(acc.Commit().HasError());
|
||||
}
|
||||
|
||||
// delete edge
|
||||
{
|
||||
auto acc = main_store.Access();
|
||||
auto v = acc.FindVertex(*vertex_gid, memgraph::storage::View::OLD);
|
||||
ASSERT_TRUE(v);
|
||||
auto out_edges = v->OutEdges(memgraph::storage::View::OLD);
|
||||
auto edge = find_edge(*out_edges, *edge_gid);
|
||||
ASSERT_TRUE(edge);
|
||||
ASSERT_TRUE(acc.DeleteEdge(&*edge).HasValue());
|
||||
ASSERT_FALSE(acc.Commit().HasError());
|
||||
}
|
||||
|
||||
{
|
||||
auto acc = replica_store.Access();
|
||||
const auto v = acc.FindVertex(*vertex_gid, memgraph::storage::View::OLD);
|
||||
ASSERT_TRUE(v);
|
||||
const auto out_edges = v->OutEdges(memgraph::storage::View::OLD);
|
||||
ASSERT_TRUE(out_edges.HasValue());
|
||||
ASSERT_FALSE(find_edge(*out_edges, *edge_gid));
|
||||
ASSERT_FALSE(acc.Commit().HasError());
|
||||
}
|
||||
|
||||
// label index create
|
||||
// label property index create
|
||||
// existence constraint create
|
||||
// unique constriant create
|
||||
const auto *label = "label";
|
||||
const auto *property = "property";
|
||||
const auto *property_extra = "property_extra";
|
||||
{
|
||||
ASSERT_TRUE(main_store.CreateIndex(main_store.NameToLabel(label)));
|
||||
ASSERT_TRUE(main_store.CreateIndex(main_store.NameToLabel(label), main_store.NameToProperty(property)));
|
||||
ASSERT_FALSE(
|
||||
main_store.CreateExistenceConstraint(main_store.NameToLabel(label), main_store.NameToProperty(property))
|
||||
.HasError());
|
||||
ASSERT_FALSE(main_store
|
||||
.CreateUniqueConstraint(main_store.NameToLabel(label), {main_store.NameToProperty(property),
|
||||
main_store.NameToProperty(property_extra)})
|
||||
.HasError());
|
||||
}
|
||||
|
||||
{
|
||||
const auto indices = replica_store.ListAllIndices();
|
||||
ASSERT_THAT(indices.label, UnorderedElementsAre(replica_store.NameToLabel(label)));
|
||||
ASSERT_THAT(indices.label_property, UnorderedElementsAre(std::make_pair(replica_store.NameToLabel(label),
|
||||
replica_store.NameToProperty(property))));
|
||||
|
||||
const auto constraints = replica_store.ListAllConstraints();
|
||||
ASSERT_THAT(constraints.existence, UnorderedElementsAre(std::make_pair(replica_store.NameToLabel(label),
|
||||
replica_store.NameToProperty(property))));
|
||||
ASSERT_THAT(constraints.unique,
|
||||
UnorderedElementsAre(std::make_pair(
|
||||
replica_store.NameToLabel(label),
|
||||
std::set{replica_store.NameToProperty(property), replica_store.NameToProperty(property_extra)})));
|
||||
}
|
||||
|
||||
// label index drop
|
||||
// label property index drop
|
||||
// existence constraint drop
|
||||
// unique constriant drop
|
||||
{
|
||||
ASSERT_TRUE(main_store.DropIndex(main_store.NameToLabel(label)));
|
||||
ASSERT_TRUE(main_store.DropIndex(main_store.NameToLabel(label), main_store.NameToProperty(property)));
|
||||
ASSERT_TRUE(main_store.DropExistenceConstraint(main_store.NameToLabel(label), main_store.NameToProperty(property)));
|
||||
ASSERT_EQ(
|
||||
main_store.DropUniqueConstraint(main_store.NameToLabel(label), {main_store.NameToProperty(property),
|
||||
main_store.NameToProperty(property_extra)}),
|
||||
memgraph::storage::UniqueConstraints::DeletionStatus::SUCCESS);
|
||||
}
|
||||
|
||||
{
|
||||
const auto indices = replica_store.ListAllIndices();
|
||||
ASSERT_EQ(indices.label.size(), 0);
|
||||
ASSERT_EQ(indices.label_property.size(), 0);
|
||||
|
||||
const auto constraints = replica_store.ListAllConstraints();
|
||||
ASSERT_EQ(constraints.existence.size(), 0);
|
||||
ASSERT_EQ(constraints.unique.size(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ReplicationTest, MultipleSynchronousReplicationTest) {
|
||||
memgraph::storage::Storage main_store(
|
||||
{.durability = {
|
||||
.storage_directory = storage_directory,
|
||||
.snapshot_wal_mode = memgraph::storage::Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT_WITH_WAL,
|
||||
}});
|
||||
|
||||
memgraph::storage::Storage replica_store1(
|
||||
{.durability = {
|
||||
.storage_directory = storage_directory,
|
||||
.snapshot_wal_mode = memgraph::storage::Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT_WITH_WAL,
|
||||
}});
|
||||
replica_store1.SetReplicaRole(memgraph::io::network::Endpoint{local_host, ports[0]});
|
||||
|
||||
memgraph::storage::Storage replica_store2(
|
||||
{.durability = {
|
||||
.storage_directory = storage_directory,
|
||||
.snapshot_wal_mode = memgraph::storage::Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT_WITH_WAL,
|
||||
}});
|
||||
replica_store2.SetReplicaRole(memgraph::io::network::Endpoint{local_host, ports[1]});
|
||||
|
||||
ASSERT_FALSE(main_store
|
||||
.RegisterReplica(replicas[0], memgraph::io::network::Endpoint{local_host, ports[0]},
|
||||
memgraph::storage::replication::ReplicationMode::SYNC,
|
||||
memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID)
|
||||
.HasError());
|
||||
ASSERT_FALSE(main_store
|
||||
.RegisterReplica(replicas[1], memgraph::io::network::Endpoint{local_host, ports[1]},
|
||||
memgraph::storage::replication::ReplicationMode::SYNC,
|
||||
memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID)
|
||||
.HasError());
|
||||
|
||||
const auto *vertex_label = "label";
|
||||
const auto *vertex_property = "property";
|
||||
const auto *vertex_property_value = "property_value";
|
||||
std::optional<memgraph::storage::Gid> vertex_gid;
|
||||
{
|
||||
auto acc = main_store.Access();
|
||||
auto v = acc.CreateVertex();
|
||||
ASSERT_TRUE(v.AddLabel(main_store.NameToLabel(vertex_label)).HasValue());
|
||||
ASSERT_TRUE(v.SetProperty(main_store.NameToProperty(vertex_property),
|
||||
memgraph::storage::PropertyValue(vertex_property_value))
|
||||
.HasValue());
|
||||
vertex_gid.emplace(v.Gid());
|
||||
ASSERT_FALSE(acc.Commit().HasError());
|
||||
}
|
||||
|
||||
const auto check_replica = [&](memgraph::storage::Storage *replica_store) {
|
||||
auto acc = replica_store->Access();
|
||||
const auto v = acc.FindVertex(*vertex_gid, memgraph::storage::View::OLD);
|
||||
ASSERT_TRUE(v);
|
||||
const auto labels = v->Labels(memgraph::storage::View::OLD);
|
||||
ASSERT_TRUE(labels.HasValue());
|
||||
ASSERT_THAT(*labels, UnorderedElementsAre(replica_store->NameToLabel(vertex_label)));
|
||||
ASSERT_FALSE(acc.Commit().HasError());
|
||||
};
|
||||
|
||||
check_replica(&replica_store1);
|
||||
check_replica(&replica_store2);
|
||||
|
||||
main_store.UnregisterReplica(replicas[1]);
|
||||
{
|
||||
auto acc = main_store.Access();
|
||||
auto v = acc.CreateVertex();
|
||||
vertex_gid.emplace(v.Gid());
|
||||
ASSERT_FALSE(acc.Commit().HasError());
|
||||
}
|
||||
|
||||
// REPLICA1 should contain the new vertex
|
||||
{
|
||||
auto acc = replica_store1.Access();
|
||||
const auto v = acc.FindVertex(*vertex_gid, memgraph::storage::View::OLD);
|
||||
ASSERT_TRUE(v);
|
||||
ASSERT_FALSE(acc.Commit().HasError());
|
||||
}
|
||||
|
||||
// REPLICA2 should not contain the new vertex
|
||||
{
|
||||
auto acc = replica_store2.Access();
|
||||
const auto v = acc.FindVertex(*vertex_gid, memgraph::storage::View::OLD);
|
||||
ASSERT_FALSE(v);
|
||||
ASSERT_FALSE(acc.Commit().HasError());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ReplicationTest, RecoveryProcess) {
|
||||
std::vector<memgraph::storage::Gid> vertex_gids;
|
||||
// Force the creation of snapshot
|
||||
{
|
||||
memgraph::storage::Storage main_store(
|
||||
{.durability = {
|
||||
.storage_directory = storage_directory,
|
||||
.recover_on_startup = true,
|
||||
.snapshot_wal_mode = memgraph::storage::Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT_WITH_WAL,
|
||||
.snapshot_on_exit = true,
|
||||
}});
|
||||
{
|
||||
auto acc = main_store.Access();
|
||||
// Create the vertex before registering a replica
|
||||
auto v = acc.CreateVertex();
|
||||
vertex_gids.emplace_back(v.Gid());
|
||||
ASSERT_FALSE(acc.Commit().HasError());
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// Create second WAL
|
||||
memgraph::storage::Storage main_store(
|
||||
{.durability = {
|
||||
.storage_directory = storage_directory,
|
||||
.recover_on_startup = true,
|
||||
.snapshot_wal_mode = memgraph::storage::Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT_WITH_WAL}});
|
||||
// Create vertices in 2 different transactions
|
||||
{
|
||||
auto acc = main_store.Access();
|
||||
auto v = acc.CreateVertex();
|
||||
vertex_gids.emplace_back(v.Gid());
|
||||
ASSERT_FALSE(acc.Commit().HasError());
|
||||
}
|
||||
{
|
||||
auto acc = main_store.Access();
|
||||
auto v = acc.CreateVertex();
|
||||
vertex_gids.emplace_back(v.Gid());
|
||||
ASSERT_FALSE(acc.Commit().HasError());
|
||||
}
|
||||
}
|
||||
|
||||
memgraph::storage::Storage main_store(
|
||||
{.durability = {
|
||||
.storage_directory = storage_directory,
|
||||
.recover_on_startup = true,
|
||||
.snapshot_wal_mode = memgraph::storage::Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT_WITH_WAL,
|
||||
}});
|
||||
|
||||
static constexpr const auto *property_name = "property_name";
|
||||
static constexpr const auto property_value = 1;
|
||||
{
|
||||
// Force the creation of current WAL file
|
||||
auto acc = main_store.Access();
|
||||
for (const auto &vertex_gid : vertex_gids) {
|
||||
auto v = acc.FindVertex(vertex_gid, memgraph::storage::View::OLD);
|
||||
ASSERT_TRUE(v);
|
||||
ASSERT_TRUE(
|
||||
v->SetProperty(main_store.NameToProperty(property_name), memgraph::storage::PropertyValue(property_value))
|
||||
.HasValue());
|
||||
}
|
||||
ASSERT_FALSE(acc.Commit().HasError());
|
||||
}
|
||||
|
||||
std::filesystem::path replica_storage_directory{std::filesystem::temp_directory_path() /
|
||||
"MG_test_unit_storage_v2_replication_replica"};
|
||||
memgraph::utils::OnScopeExit replica_directory_cleaner(
|
||||
[&]() { std::filesystem::remove_all(replica_storage_directory); });
|
||||
|
||||
static constexpr const auto *vertex_label = "vertex_label";
|
||||
{
|
||||
memgraph::storage::Storage replica_store(
|
||||
{.durability = {
|
||||
.storage_directory = replica_storage_directory,
|
||||
.snapshot_wal_mode = memgraph::storage::Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT_WITH_WAL}});
|
||||
|
||||
replica_store.SetReplicaRole(memgraph::io::network::Endpoint{local_host, ports[0]});
|
||||
|
||||
ASSERT_FALSE(main_store
|
||||
.RegisterReplica(replicas[0], memgraph::io::network::Endpoint{local_host, ports[0]},
|
||||
memgraph::storage::replication::ReplicationMode::SYNC,
|
||||
memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID)
|
||||
.HasError());
|
||||
|
||||
ASSERT_EQ(main_store.GetReplicaState(replicas[0]), memgraph::storage::replication::ReplicaState::RECOVERY);
|
||||
|
||||
while (main_store.GetReplicaState(replicas[0]) != memgraph::storage::replication::ReplicaState::READY) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
}
|
||||
|
||||
{
|
||||
auto acc = main_store.Access();
|
||||
for (const auto &vertex_gid : vertex_gids) {
|
||||
auto v = acc.FindVertex(vertex_gid, memgraph::storage::View::OLD);
|
||||
ASSERT_TRUE(v);
|
||||
ASSERT_TRUE(v->AddLabel(main_store.NameToLabel(vertex_label)).HasValue());
|
||||
}
|
||||
ASSERT_FALSE(acc.Commit().HasError());
|
||||
}
|
||||
{
|
||||
auto acc = replica_store.Access();
|
||||
for (const auto &vertex_gid : vertex_gids) {
|
||||
auto v = acc.FindVertex(vertex_gid, memgraph::storage::View::OLD);
|
||||
ASSERT_TRUE(v);
|
||||
const auto labels = v->Labels(memgraph::storage::View::OLD);
|
||||
ASSERT_TRUE(labels.HasValue());
|
||||
ASSERT_THAT(*labels, UnorderedElementsAre(replica_store.NameToLabel(vertex_label)));
|
||||
const auto properties = v->Properties(memgraph::storage::View::OLD);
|
||||
ASSERT_TRUE(properties.HasValue());
|
||||
ASSERT_THAT(*properties,
|
||||
UnorderedElementsAre(std::make_pair(replica_store.NameToProperty(property_name),
|
||||
memgraph::storage::PropertyValue(property_value))));
|
||||
}
|
||||
ASSERT_FALSE(acc.Commit().HasError());
|
||||
}
|
||||
}
|
||||
{
|
||||
memgraph::storage::Storage replica_store(
|
||||
{.durability = {
|
||||
.storage_directory = replica_storage_directory,
|
||||
.recover_on_startup = true,
|
||||
.snapshot_wal_mode = memgraph::storage::Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT_WITH_WAL}});
|
||||
{
|
||||
auto acc = replica_store.Access();
|
||||
for (const auto &vertex_gid : vertex_gids) {
|
||||
auto v = acc.FindVertex(vertex_gid, memgraph::storage::View::OLD);
|
||||
ASSERT_TRUE(v);
|
||||
const auto labels = v->Labels(memgraph::storage::View::OLD);
|
||||
ASSERT_TRUE(labels.HasValue());
|
||||
ASSERT_THAT(*labels, UnorderedElementsAre(replica_store.NameToLabel(vertex_label)));
|
||||
const auto properties = v->Properties(memgraph::storage::View::OLD);
|
||||
ASSERT_TRUE(properties.HasValue());
|
||||
ASSERT_THAT(*properties,
|
||||
UnorderedElementsAre(std::make_pair(replica_store.NameToProperty(property_name),
|
||||
memgraph::storage::PropertyValue(property_value))));
|
||||
}
|
||||
ASSERT_FALSE(acc.Commit().HasError());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ReplicationTest, BasicAsynchronousReplicationTest) {
|
||||
memgraph::storage::Storage main_store(configuration);
|
||||
|
||||
memgraph::storage::Storage replica_store_async(configuration);
|
||||
|
||||
replica_store_async.SetReplicaRole(memgraph::io::network::Endpoint{local_host, ports[1]});
|
||||
|
||||
ASSERT_FALSE(main_store
|
||||
.RegisterReplica("REPLICA_ASYNC", memgraph::io::network::Endpoint{local_host, ports[1]},
|
||||
memgraph::storage::replication::ReplicationMode::ASYNC,
|
||||
memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID)
|
||||
.HasError());
|
||||
|
||||
static constexpr size_t vertices_create_num = 10;
|
||||
std::vector<memgraph::storage::Gid> created_vertices;
|
||||
for (size_t i = 0; i < vertices_create_num; ++i) {
|
||||
auto acc = main_store.Access();
|
||||
auto v = acc.CreateVertex();
|
||||
created_vertices.push_back(v.Gid());
|
||||
ASSERT_FALSE(acc.Commit().HasError());
|
||||
|
||||
if (i == 0) {
|
||||
ASSERT_EQ(main_store.GetReplicaState("REPLICA_ASYNC"), memgraph::storage::replication::ReplicaState::REPLICATING);
|
||||
} else {
|
||||
ASSERT_EQ(main_store.GetReplicaState("REPLICA_ASYNC"), memgraph::storage::replication::ReplicaState::RECOVERY);
|
||||
}
|
||||
}
|
||||
|
||||
while (main_store.GetReplicaState("REPLICA_ASYNC") != memgraph::storage::replication::ReplicaState::READY) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
}
|
||||
|
||||
ASSERT_TRUE(std::all_of(created_vertices.begin(), created_vertices.end(), [&](const auto vertex_gid) {
|
||||
auto acc = replica_store_async.Access();
|
||||
auto v = acc.FindVertex(vertex_gid, memgraph::storage::View::OLD);
|
||||
const bool exists = v.has_value();
|
||||
EXPECT_FALSE(acc.Commit().HasError());
|
||||
return exists;
|
||||
}));
|
||||
}
|
||||
|
||||
TEST_F(ReplicationTest, EpochTest) {
|
||||
memgraph::storage::Storage main_store(configuration);
|
||||
|
||||
memgraph::storage::Storage replica_store1(configuration);
|
||||
|
||||
replica_store1.SetReplicaRole(memgraph::io::network::Endpoint{local_host, ports[0]});
|
||||
|
||||
memgraph::storage::Storage replica_store2(configuration);
|
||||
|
||||
replica_store2.SetReplicaRole(memgraph::io::network::Endpoint{local_host, 10001});
|
||||
|
||||
ASSERT_FALSE(main_store
|
||||
.RegisterReplica(replicas[0], memgraph::io::network::Endpoint{local_host, ports[0]},
|
||||
memgraph::storage::replication::ReplicationMode::SYNC,
|
||||
memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID)
|
||||
.HasError());
|
||||
|
||||
ASSERT_FALSE(main_store
|
||||
.RegisterReplica(replicas[1], memgraph::io::network::Endpoint{local_host, 10001},
|
||||
memgraph::storage::replication::ReplicationMode::SYNC,
|
||||
memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID)
|
||||
.HasError());
|
||||
|
||||
std::optional<memgraph::storage::Gid> vertex_gid;
|
||||
{
|
||||
auto acc = main_store.Access();
|
||||
const auto v = acc.CreateVertex();
|
||||
vertex_gid.emplace(v.Gid());
|
||||
ASSERT_FALSE(acc.Commit().HasError());
|
||||
}
|
||||
{
|
||||
auto acc = replica_store1.Access();
|
||||
const auto v = acc.FindVertex(*vertex_gid, memgraph::storage::View::OLD);
|
||||
ASSERT_TRUE(v);
|
||||
ASSERT_FALSE(acc.Commit().HasError());
|
||||
}
|
||||
{
|
||||
auto acc = replica_store2.Access();
|
||||
const auto v = acc.FindVertex(*vertex_gid, memgraph::storage::View::OLD);
|
||||
ASSERT_TRUE(v);
|
||||
ASSERT_FALSE(acc.Commit().HasError());
|
||||
}
|
||||
|
||||
main_store.UnregisterReplica(replicas[0]);
|
||||
main_store.UnregisterReplica(replicas[1]);
|
||||
|
||||
replica_store1.SetMainReplicationRole();
|
||||
ASSERT_FALSE(replica_store1
|
||||
.RegisterReplica(replicas[1], memgraph::io::network::Endpoint{local_host, 10001},
|
||||
memgraph::storage::replication::ReplicationMode::SYNC,
|
||||
memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID)
|
||||
.HasError());
|
||||
|
||||
{
|
||||
auto acc = main_store.Access();
|
||||
acc.CreateVertex();
|
||||
ASSERT_FALSE(acc.Commit().HasError());
|
||||
}
|
||||
{
|
||||
auto acc = replica_store1.Access();
|
||||
auto v = acc.CreateVertex();
|
||||
vertex_gid.emplace(v.Gid());
|
||||
ASSERT_FALSE(acc.Commit().HasError());
|
||||
}
|
||||
// Replica1 should forward it's vertex to Replica2
|
||||
{
|
||||
auto acc = replica_store2.Access();
|
||||
const auto v = acc.FindVertex(*vertex_gid, memgraph::storage::View::OLD);
|
||||
ASSERT_TRUE(v);
|
||||
ASSERT_FALSE(acc.Commit().HasError());
|
||||
}
|
||||
|
||||
replica_store1.SetReplicaRole(memgraph::io::network::Endpoint{local_host, ports[0]});
|
||||
ASSERT_TRUE(main_store
|
||||
.RegisterReplica(replicas[0], memgraph::io::network::Endpoint{local_host, ports[0]},
|
||||
memgraph::storage::replication::ReplicationMode::SYNC,
|
||||
memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID)
|
||||
.HasError());
|
||||
|
||||
{
|
||||
auto acc = main_store.Access();
|
||||
const auto v = acc.CreateVertex();
|
||||
vertex_gid.emplace(v.Gid());
|
||||
ASSERT_FALSE(acc.Commit().HasError());
|
||||
}
|
||||
// Replica1 is not compatible with the main so it shouldn't contain
|
||||
// it's newest vertex
|
||||
{
|
||||
auto acc = replica_store1.Access();
|
||||
const auto v = acc.FindVertex(*vertex_gid, memgraph::storage::View::OLD);
|
||||
ASSERT_FALSE(v);
|
||||
ASSERT_FALSE(acc.Commit().HasError());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ReplicationTest, ReplicationInformation) {
|
||||
memgraph::storage::Storage main_store(configuration);
|
||||
|
||||
memgraph::storage::Storage replica_store1(configuration);
|
||||
|
||||
const memgraph::io::network::Endpoint replica1_endpoint{local_host, 10001};
|
||||
replica_store1.SetReplicaRole(replica1_endpoint);
|
||||
|
||||
const memgraph::io::network::Endpoint replica2_endpoint{local_host, 10002};
|
||||
memgraph::storage::Storage replica_store2(configuration);
|
||||
|
||||
replica_store2.SetReplicaRole(replica2_endpoint);
|
||||
|
||||
const std::string replica1_name{replicas[0]};
|
||||
ASSERT_FALSE(main_store
|
||||
.RegisterReplica(replica1_name, replica1_endpoint,
|
||||
memgraph::storage::replication::ReplicationMode::SYNC,
|
||||
memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID)
|
||||
.HasError());
|
||||
|
||||
const std::string replica2_name{replicas[1]};
|
||||
ASSERT_FALSE(main_store
|
||||
.RegisterReplica(replica2_name, replica2_endpoint,
|
||||
memgraph::storage::replication::ReplicationMode::ASYNC,
|
||||
memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID)
|
||||
.HasError());
|
||||
|
||||
ASSERT_EQ(main_store.GetReplicationRole(), memgraph::storage::ReplicationRole::MAIN);
|
||||
ASSERT_EQ(replica_store1.GetReplicationRole(), memgraph::storage::ReplicationRole::REPLICA);
|
||||
ASSERT_EQ(replica_store2.GetReplicationRole(), memgraph::storage::ReplicationRole::REPLICA);
|
||||
|
||||
const auto replicas_info = main_store.ReplicasInfo();
|
||||
ASSERT_EQ(replicas_info.size(), 2);
|
||||
|
||||
const auto &first_info = replicas_info[0];
|
||||
ASSERT_EQ(first_info.name, replica1_name);
|
||||
ASSERT_EQ(first_info.mode, memgraph::storage::replication::ReplicationMode::SYNC);
|
||||
ASSERT_EQ(first_info.endpoint, replica1_endpoint);
|
||||
ASSERT_EQ(first_info.state, memgraph::storage::replication::ReplicaState::READY);
|
||||
|
||||
const auto &second_info = replicas_info[1];
|
||||
ASSERT_EQ(second_info.name, replica2_name);
|
||||
ASSERT_EQ(second_info.mode, memgraph::storage::replication::ReplicationMode::ASYNC);
|
||||
ASSERT_EQ(second_info.endpoint, replica2_endpoint);
|
||||
ASSERT_EQ(second_info.state, memgraph::storage::replication::ReplicaState::READY);
|
||||
}
|
||||
|
||||
TEST_F(ReplicationTest, ReplicationReplicaWithExistingName) {
|
||||
memgraph::storage::Storage main_store(configuration);
|
||||
|
||||
memgraph::storage::Storage replica_store1(configuration);
|
||||
|
||||
const memgraph::io::network::Endpoint replica1_endpoint{local_host, 10001};
|
||||
replica_store1.SetReplicaRole(replica1_endpoint);
|
||||
|
||||
const memgraph::io::network::Endpoint replica2_endpoint{local_host, 10002};
|
||||
memgraph::storage::Storage replica_store2(configuration);
|
||||
|
||||
replica_store2.SetReplicaRole(replica2_endpoint);
|
||||
|
||||
const std::string replica1_name{replicas[0]};
|
||||
ASSERT_FALSE(main_store
|
||||
.RegisterReplica(replica1_name, replica1_endpoint,
|
||||
memgraph::storage::replication::ReplicationMode::SYNC,
|
||||
memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID)
|
||||
.HasError());
|
||||
|
||||
const std::string replica2_name{replicas[0]};
|
||||
ASSERT_TRUE(main_store
|
||||
.RegisterReplica(replica2_name, replica2_endpoint,
|
||||
memgraph::storage::replication::ReplicationMode::ASYNC,
|
||||
memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID)
|
||||
.GetError() == memgraph::storage::Storage::RegisterReplicaError::NAME_EXISTS);
|
||||
}
|
||||
|
||||
TEST_F(ReplicationTest, ReplicationReplicaWithExistingEndPoint) {
|
||||
memgraph::storage::Storage main_store(configuration);
|
||||
|
||||
memgraph::storage::Storage replica_store1(configuration);
|
||||
|
||||
const memgraph::io::network::Endpoint replica1_endpoint{local_host, 10001};
|
||||
replica_store1.SetReplicaRole(replica1_endpoint);
|
||||
|
||||
const memgraph::io::network::Endpoint replica2_endpoint{local_host, 10001};
|
||||
memgraph::storage::Storage replica_store2(configuration);
|
||||
|
||||
replica_store2.SetReplicaRole(replica2_endpoint);
|
||||
|
||||
const std::string replica1_name{replicas[0]};
|
||||
ASSERT_FALSE(main_store
|
||||
.RegisterReplica(replica1_name, replica1_endpoint,
|
||||
memgraph::storage::replication::ReplicationMode::SYNC,
|
||||
memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID)
|
||||
.HasError());
|
||||
|
||||
const std::string replica2_name{replicas[1]};
|
||||
ASSERT_TRUE(main_store
|
||||
.RegisterReplica(replica2_name, replica2_endpoint,
|
||||
memgraph::storage::replication::ReplicationMode::ASYNC,
|
||||
memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID)
|
||||
.GetError() == memgraph::storage::Storage::RegisterReplicaError::END_POINT_EXISTS);
|
||||
}
|
||||
|
||||
TEST_F(ReplicationTest, RestoringReplicationAtStartupAftgerDroppingReplica) {
|
||||
auto main_config = configuration;
|
||||
main_config.durability.restore_replicas_on_startup = true;
|
||||
auto main_store = std::make_unique<memgraph::storage::Storage>(main_config);
|
||||
|
||||
memgraph::storage::Storage replica_store1(configuration);
|
||||
replica_store1.SetReplicaRole(memgraph::io::network::Endpoint{local_host, ports[0]});
|
||||
|
||||
memgraph::storage::Storage replica_store2(configuration);
|
||||
replica_store2.SetReplicaRole(memgraph::io::network::Endpoint{local_host, ports[1]});
|
||||
|
||||
auto res = main_store->RegisterReplica(replicas[0], memgraph::io::network::Endpoint{local_host, ports[0]},
|
||||
memgraph::storage::replication::ReplicationMode::SYNC,
|
||||
memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID);
|
||||
ASSERT_FALSE(res.HasError());
|
||||
res = main_store->RegisterReplica(replicas[1], memgraph::io::network::Endpoint{local_host, ports[1]},
|
||||
memgraph::storage::replication::ReplicationMode::SYNC,
|
||||
memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID);
|
||||
ASSERT_FALSE(res.HasError());
|
||||
|
||||
auto replica_infos = main_store->ReplicasInfo();
|
||||
|
||||
ASSERT_EQ(replica_infos.size(), 2);
|
||||
ASSERT_EQ(replica_infos[0].name, replicas[0]);
|
||||
ASSERT_EQ(replica_infos[0].endpoint.address, local_host);
|
||||
ASSERT_EQ(replica_infos[0].endpoint.port, ports[0]);
|
||||
ASSERT_EQ(replica_infos[1].name, replicas[1]);
|
||||
ASSERT_EQ(replica_infos[1].endpoint.address, local_host);
|
||||
ASSERT_EQ(replica_infos[1].endpoint.port, ports[1]);
|
||||
|
||||
main_store.reset();
|
||||
|
||||
auto other_main_store = std::make_unique<memgraph::storage::Storage>(main_config);
|
||||
replica_infos = other_main_store->ReplicasInfo();
|
||||
ASSERT_EQ(replica_infos.size(), 2);
|
||||
ASSERT_EQ(replica_infos[0].name, replicas[0]);
|
||||
ASSERT_EQ(replica_infos[0].endpoint.address, local_host);
|
||||
ASSERT_EQ(replica_infos[0].endpoint.port, ports[0]);
|
||||
ASSERT_EQ(replica_infos[1].name, replicas[1]);
|
||||
ASSERT_EQ(replica_infos[1].endpoint.address, local_host);
|
||||
ASSERT_EQ(replica_infos[1].endpoint.port, ports[1]);
|
||||
}
|
||||
|
||||
TEST_F(ReplicationTest, RestoringReplicationAtStartup) {
|
||||
auto main_config = configuration;
|
||||
main_config.durability.restore_replicas_on_startup = true;
|
||||
auto main_store = std::make_unique<memgraph::storage::Storage>(main_config);
|
||||
memgraph::storage::Storage replica_store1(configuration);
|
||||
replica_store1.SetReplicaRole(memgraph::io::network::Endpoint{local_host, ports[0]});
|
||||
|
||||
memgraph::storage::Storage replica_store2(configuration);
|
||||
replica_store2.SetReplicaRole(memgraph::io::network::Endpoint{local_host, ports[1]});
|
||||
|
||||
auto res = main_store->RegisterReplica(replicas[0], memgraph::io::network::Endpoint{local_host, ports[0]},
|
||||
memgraph::storage::replication::ReplicationMode::SYNC,
|
||||
memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID);
|
||||
ASSERT_FALSE(res.HasError());
|
||||
res = main_store->RegisterReplica(replicas[1], memgraph::io::network::Endpoint{local_host, ports[1]},
|
||||
memgraph::storage::replication::ReplicationMode::SYNC,
|
||||
memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID);
|
||||
ASSERT_FALSE(res.HasError());
|
||||
|
||||
auto replica_infos = main_store->ReplicasInfo();
|
||||
|
||||
ASSERT_EQ(replica_infos.size(), 2);
|
||||
ASSERT_EQ(replica_infos[0].name, replicas[0]);
|
||||
ASSERT_EQ(replica_infos[0].endpoint.address, local_host);
|
||||
ASSERT_EQ(replica_infos[0].endpoint.port, ports[0]);
|
||||
ASSERT_EQ(replica_infos[1].name, replicas[1]);
|
||||
ASSERT_EQ(replica_infos[1].endpoint.address, local_host);
|
||||
ASSERT_EQ(replica_infos[1].endpoint.port, ports[1]);
|
||||
|
||||
const auto unregister_res = main_store->UnregisterReplica(replicas[0]);
|
||||
ASSERT_TRUE(unregister_res);
|
||||
|
||||
replica_infos = main_store->ReplicasInfo();
|
||||
ASSERT_EQ(replica_infos.size(), 1);
|
||||
ASSERT_EQ(replica_infos[0].name, replicas[1]);
|
||||
ASSERT_EQ(replica_infos[0].endpoint.address, local_host);
|
||||
ASSERT_EQ(replica_infos[0].endpoint.port, ports[1]);
|
||||
|
||||
main_store.reset();
|
||||
|
||||
auto other_main_store = std::make_unique<memgraph::storage::Storage>(main_config);
|
||||
replica_infos = other_main_store->ReplicasInfo();
|
||||
ASSERT_EQ(replica_infos.size(), 1);
|
||||
ASSERT_EQ(replica_infos[0].name, replicas[1]);
|
||||
ASSERT_EQ(replica_infos[0].endpoint.address, local_host);
|
||||
ASSERT_EQ(replica_infos[0].endpoint.port, ports[1]);
|
||||
}
|
||||
|
||||
TEST_F(ReplicationTest, AddingInvalidReplica) {
|
||||
memgraph::storage::Storage main_store(configuration);
|
||||
|
||||
ASSERT_TRUE(main_store
|
||||
.RegisterReplica("REPLICA", memgraph::io::network::Endpoint{local_host, ports[0]},
|
||||
memgraph::storage::replication::ReplicationMode::SYNC,
|
||||
memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID)
|
||||
.GetError() == memgraph::storage::Storage::RegisterReplicaError::CONNECTION_FAILED);
|
||||
}
|
@ -1,623 +0,0 @@
|
||||
// Copyright 2022 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 <gtest/gtest.h>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
#include <string_view>
|
||||
|
||||
#include "storage/v2/durability/exceptions.hpp"
|
||||
#include "storage/v2/durability/version.hpp"
|
||||
#include "storage/v2/durability/wal.hpp"
|
||||
#include "storage/v2/mvcc.hpp"
|
||||
#include "storage/v2/name_id_mapper.hpp"
|
||||
#include "utils/file.hpp"
|
||||
#include "utils/file_locker.hpp"
|
||||
#include "utils/uuid.hpp"
|
||||
|
||||
// Helper function used to convert between enum types.
|
||||
memgraph::storage::durability::WalDeltaData::Type StorageGlobalOperationToWalDeltaDataType(
|
||||
memgraph::storage::durability::StorageGlobalOperation operation) {
|
||||
switch (operation) {
|
||||
case memgraph::storage::durability::StorageGlobalOperation::LABEL_INDEX_CREATE:
|
||||
return memgraph::storage::durability::WalDeltaData::Type::LABEL_INDEX_CREATE;
|
||||
case memgraph::storage::durability::StorageGlobalOperation::LABEL_INDEX_DROP:
|
||||
return memgraph::storage::durability::WalDeltaData::Type::LABEL_INDEX_DROP;
|
||||
case memgraph::storage::durability::StorageGlobalOperation::LABEL_PROPERTY_INDEX_CREATE:
|
||||
return memgraph::storage::durability::WalDeltaData::Type::LABEL_PROPERTY_INDEX_CREATE;
|
||||
case memgraph::storage::durability::StorageGlobalOperation::LABEL_PROPERTY_INDEX_DROP:
|
||||
return memgraph::storage::durability::WalDeltaData::Type::LABEL_PROPERTY_INDEX_DROP;
|
||||
case memgraph::storage::durability::StorageGlobalOperation::EXISTENCE_CONSTRAINT_CREATE:
|
||||
return memgraph::storage::durability::WalDeltaData::Type::EXISTENCE_CONSTRAINT_CREATE;
|
||||
case memgraph::storage::durability::StorageGlobalOperation::EXISTENCE_CONSTRAINT_DROP:
|
||||
return memgraph::storage::durability::WalDeltaData::Type::EXISTENCE_CONSTRAINT_DROP;
|
||||
case memgraph::storage::durability::StorageGlobalOperation::UNIQUE_CONSTRAINT_CREATE:
|
||||
return memgraph::storage::durability::WalDeltaData::Type::UNIQUE_CONSTRAINT_CREATE;
|
||||
case memgraph::storage::durability::StorageGlobalOperation::UNIQUE_CONSTRAINT_DROP:
|
||||
return memgraph::storage::durability::WalDeltaData::Type::UNIQUE_CONSTRAINT_DROP;
|
||||
}
|
||||
}
|
||||
|
||||
// This class mimics the internals of the storage to generate the deltas.
|
||||
class DeltaGenerator final {
|
||||
public:
|
||||
class Transaction final {
|
||||
private:
|
||||
friend class DeltaGenerator;
|
||||
|
||||
explicit Transaction(DeltaGenerator *gen)
|
||||
: gen_(gen),
|
||||
transaction_(gen->transaction_id_++, gen->timestamp_++,
|
||||
memgraph::storage::IsolationLevel::SNAPSHOT_ISOLATION) {}
|
||||
|
||||
public:
|
||||
memgraph::storage::Vertex *CreateVertex() {
|
||||
auto gid = memgraph::storage::Gid::FromUint(gen_->vertices_count_++);
|
||||
auto delta = memgraph::storage::CreateDeleteObjectDelta(&transaction_);
|
||||
auto &it = gen_->vertices_.emplace_back(gid, delta);
|
||||
delta->prev.Set(&it);
|
||||
{
|
||||
memgraph::storage::durability::WalDeltaData data;
|
||||
data.type = memgraph::storage::durability::WalDeltaData::Type::VERTEX_CREATE;
|
||||
data.vertex_create_delete.gid = gid;
|
||||
data_.push_back(data);
|
||||
}
|
||||
return ⁢
|
||||
}
|
||||
|
||||
void DeleteVertex(memgraph::storage::Vertex *vertex) {
|
||||
memgraph::storage::CreateAndLinkDelta(&transaction_, &*vertex, memgraph::storage::Delta::RecreateObjectTag());
|
||||
{
|
||||
memgraph::storage::durability::WalDeltaData data;
|
||||
data.type = memgraph::storage::durability::WalDeltaData::Type::VERTEX_DELETE;
|
||||
data.vertex_create_delete.gid = vertex->gid;
|
||||
data_.push_back(data);
|
||||
}
|
||||
}
|
||||
|
||||
void AddLabel(memgraph::storage::Vertex *vertex, const std::string &label) {
|
||||
auto label_id = memgraph::storage::LabelId::FromUint(gen_->mapper_.NameToId(label));
|
||||
vertex->labels.push_back(label_id);
|
||||
memgraph::storage::CreateAndLinkDelta(&transaction_, &*vertex, memgraph::storage::Delta::RemoveLabelTag(),
|
||||
label_id);
|
||||
{
|
||||
memgraph::storage::durability::WalDeltaData data;
|
||||
data.type = memgraph::storage::durability::WalDeltaData::Type::VERTEX_ADD_LABEL;
|
||||
data.vertex_add_remove_label.gid = vertex->gid;
|
||||
data.vertex_add_remove_label.label = label;
|
||||
data_.push_back(data);
|
||||
}
|
||||
}
|
||||
|
||||
void RemoveLabel(memgraph::storage::Vertex *vertex, const std::string &label) {
|
||||
auto label_id = memgraph::storage::LabelId::FromUint(gen_->mapper_.NameToId(label));
|
||||
vertex->labels.erase(std::find(vertex->labels.begin(), vertex->labels.end(), label_id));
|
||||
memgraph::storage::CreateAndLinkDelta(&transaction_, &*vertex, memgraph::storage::Delta::AddLabelTag(), label_id);
|
||||
{
|
||||
memgraph::storage::durability::WalDeltaData data;
|
||||
data.type = memgraph::storage::durability::WalDeltaData::Type::VERTEX_REMOVE_LABEL;
|
||||
data.vertex_add_remove_label.gid = vertex->gid;
|
||||
data.vertex_add_remove_label.label = label;
|
||||
data_.push_back(data);
|
||||
}
|
||||
}
|
||||
|
||||
void SetProperty(memgraph::storage::Vertex *vertex, const std::string &property,
|
||||
const memgraph::storage::PropertyValue &value) {
|
||||
auto property_id = memgraph::storage::PropertyId::FromUint(gen_->mapper_.NameToId(property));
|
||||
auto &props = vertex->properties;
|
||||
auto old_value = props.GetProperty(property_id);
|
||||
memgraph::storage::CreateAndLinkDelta(&transaction_, &*vertex, memgraph::storage::Delta::SetPropertyTag(),
|
||||
property_id, old_value);
|
||||
props.SetProperty(property_id, value);
|
||||
{
|
||||
memgraph::storage::durability::WalDeltaData data;
|
||||
data.type = memgraph::storage::durability::WalDeltaData::Type::VERTEX_SET_PROPERTY;
|
||||
data.vertex_edge_set_property.gid = vertex->gid;
|
||||
data.vertex_edge_set_property.property = property;
|
||||
// We don't store the property value here. That is because the storage
|
||||
// generates multiple `SetProperty` deltas using only the final values
|
||||
// of the property. The intermediate values aren't encoded. The value is
|
||||
// later determined in the `Finalize` function.
|
||||
data_.push_back(data);
|
||||
}
|
||||
}
|
||||
|
||||
void Finalize(bool append_transaction_end = true) {
|
||||
auto commit_timestamp = gen_->timestamp_++;
|
||||
for (const auto &delta : transaction_.deltas) {
|
||||
auto owner = delta.prev.Get();
|
||||
while (owner.type == memgraph::storage::PreviousPtr::Type::DELTA) {
|
||||
owner = owner.delta->prev.Get();
|
||||
}
|
||||
if (owner.type == memgraph::storage::PreviousPtr::Type::VERTEX) {
|
||||
gen_->wal_file_.AppendDelta(delta, *owner.vertex, commit_timestamp);
|
||||
} else if (owner.type == memgraph::storage::PreviousPtr::Type::EDGE) {
|
||||
gen_->wal_file_.AppendDelta(delta, *owner.edge, commit_timestamp);
|
||||
} else {
|
||||
LOG_FATAL("Invalid delta owner!");
|
||||
}
|
||||
}
|
||||
if (append_transaction_end) {
|
||||
gen_->wal_file_.AppendTransactionEnd(commit_timestamp);
|
||||
if (gen_->valid_) {
|
||||
gen_->UpdateStats(commit_timestamp, transaction_.deltas.size() + 1);
|
||||
for (auto &data : data_) {
|
||||
if (data.type == memgraph::storage::durability::WalDeltaData::Type::VERTEX_SET_PROPERTY) {
|
||||
// We need to put the final property value into the SET_PROPERTY
|
||||
// delta.
|
||||
auto vertex =
|
||||
std::find(gen_->vertices_.begin(), gen_->vertices_.end(), data.vertex_edge_set_property.gid);
|
||||
ASSERT_NE(vertex, gen_->vertices_.end());
|
||||
auto property_id = memgraph::storage::PropertyId::FromUint(
|
||||
gen_->mapper_.NameToId(data.vertex_edge_set_property.property));
|
||||
data.vertex_edge_set_property.value = vertex->properties.GetProperty(property_id);
|
||||
}
|
||||
gen_->data_.emplace_back(commit_timestamp, data);
|
||||
}
|
||||
memgraph::storage::durability::WalDeltaData data{
|
||||
.type = memgraph::storage::durability::WalDeltaData::Type::TRANSACTION_END};
|
||||
gen_->data_.emplace_back(commit_timestamp, data);
|
||||
}
|
||||
} else {
|
||||
gen_->valid_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
DeltaGenerator *gen_;
|
||||
memgraph::storage::Transaction transaction_;
|
||||
std::vector<memgraph::storage::durability::WalDeltaData> data_;
|
||||
};
|
||||
|
||||
using DataT = std::vector<std::pair<uint64_t, memgraph::storage::durability::WalDeltaData>>;
|
||||
|
||||
DeltaGenerator(const std::filesystem::path &data_directory, bool properties_on_edges, uint64_t seq_num)
|
||||
: uuid_(memgraph::utils::GenerateUUID()),
|
||||
epoch_id_(memgraph::utils::GenerateUUID()),
|
||||
seq_num_(seq_num),
|
||||
wal_file_(data_directory, uuid_, epoch_id_, {.properties_on_edges = properties_on_edges}, &mapper_, seq_num,
|
||||
&file_retainer_) {}
|
||||
|
||||
Transaction CreateTransaction() { return Transaction(this); }
|
||||
|
||||
void ResetTransactionIds() {
|
||||
transaction_id_ = memgraph::storage::kTransactionInitialId;
|
||||
timestamp_ = memgraph::storage::kTimestampInitialId;
|
||||
valid_ = false;
|
||||
}
|
||||
|
||||
void AppendOperation(memgraph::storage::durability::StorageGlobalOperation operation, const std::string &label,
|
||||
const std::set<std::string> properties = {}) {
|
||||
auto label_id = memgraph::storage::LabelId::FromUint(mapper_.NameToId(label));
|
||||
std::set<memgraph::storage::PropertyId> property_ids;
|
||||
for (const auto &property : properties) {
|
||||
property_ids.insert(memgraph::storage::PropertyId::FromUint(mapper_.NameToId(property)));
|
||||
}
|
||||
wal_file_.AppendOperation(operation, label_id, property_ids, timestamp_);
|
||||
if (valid_) {
|
||||
UpdateStats(timestamp_, 1);
|
||||
memgraph::storage::durability::WalDeltaData data;
|
||||
data.type = StorageGlobalOperationToWalDeltaDataType(operation);
|
||||
switch (operation) {
|
||||
case memgraph::storage::durability::StorageGlobalOperation::LABEL_INDEX_CREATE:
|
||||
case memgraph::storage::durability::StorageGlobalOperation::LABEL_INDEX_DROP:
|
||||
data.operation_label.label = label;
|
||||
break;
|
||||
case memgraph::storage::durability::StorageGlobalOperation::LABEL_PROPERTY_INDEX_CREATE:
|
||||
case memgraph::storage::durability::StorageGlobalOperation::LABEL_PROPERTY_INDEX_DROP:
|
||||
case memgraph::storage::durability::StorageGlobalOperation::EXISTENCE_CONSTRAINT_CREATE:
|
||||
case memgraph::storage::durability::StorageGlobalOperation::EXISTENCE_CONSTRAINT_DROP:
|
||||
data.operation_label_property.label = label;
|
||||
data.operation_label_property.property = *properties.begin();
|
||||
case memgraph::storage::durability::StorageGlobalOperation::UNIQUE_CONSTRAINT_CREATE:
|
||||
case memgraph::storage::durability::StorageGlobalOperation::UNIQUE_CONSTRAINT_DROP:
|
||||
data.operation_label_properties.label = label;
|
||||
data.operation_label_properties.properties = properties;
|
||||
}
|
||||
data_.emplace_back(timestamp_, data);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t GetPosition() { return wal_file_.GetSize(); }
|
||||
|
||||
memgraph::storage::durability::WalInfo GetInfo() {
|
||||
return {.offset_metadata = 0,
|
||||
.offset_deltas = 0,
|
||||
.uuid = uuid_,
|
||||
.epoch_id = epoch_id_,
|
||||
.seq_num = seq_num_,
|
||||
.from_timestamp = tx_from_,
|
||||
.to_timestamp = tx_to_,
|
||||
.num_deltas = deltas_count_};
|
||||
}
|
||||
|
||||
DataT GetData() { return data_; }
|
||||
|
||||
private:
|
||||
void UpdateStats(uint64_t timestamp, uint64_t count) {
|
||||
if (deltas_count_ == 0) {
|
||||
tx_from_ = timestamp;
|
||||
}
|
||||
tx_to_ = timestamp;
|
||||
deltas_count_ += count;
|
||||
}
|
||||
|
||||
std::string uuid_;
|
||||
std::string epoch_id_;
|
||||
uint64_t seq_num_;
|
||||
|
||||
uint64_t transaction_id_{memgraph::storage::kTransactionInitialId};
|
||||
uint64_t timestamp_{memgraph::storage::kTimestampInitialId};
|
||||
uint64_t vertices_count_{0};
|
||||
std::list<memgraph::storage::Vertex> vertices_;
|
||||
memgraph::storage::NameIdMapper mapper_;
|
||||
|
||||
memgraph::storage::durability::WalFile wal_file_;
|
||||
|
||||
DataT data_;
|
||||
|
||||
uint64_t deltas_count_{0};
|
||||
uint64_t tx_from_{0};
|
||||
uint64_t tx_to_{0};
|
||||
uint64_t valid_{true};
|
||||
|
||||
memgraph::utils::FileRetainer file_retainer_;
|
||||
};
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
|
||||
#define TRANSACTION(append_transaction_end, ops) \
|
||||
{ \
|
||||
auto tx = gen.CreateTransaction(); \
|
||||
ops; \
|
||||
tx.Finalize(append_transaction_end); \
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
|
||||
#define OPERATION(op, ...) gen.AppendOperation(memgraph::storage::durability::StorageGlobalOperation::op, __VA_ARGS__)
|
||||
|
||||
void AssertWalInfoEqual(const memgraph::storage::durability::WalInfo &a,
|
||||
const memgraph::storage::durability::WalInfo &b) {
|
||||
ASSERT_EQ(a.uuid, b.uuid);
|
||||
ASSERT_EQ(a.epoch_id, b.epoch_id);
|
||||
ASSERT_EQ(a.seq_num, b.seq_num);
|
||||
ASSERT_EQ(a.from_timestamp, b.from_timestamp);
|
||||
ASSERT_EQ(a.to_timestamp, b.to_timestamp);
|
||||
ASSERT_EQ(a.num_deltas, b.num_deltas);
|
||||
}
|
||||
|
||||
void AssertWalDataEqual(const DeltaGenerator::DataT &data, const std::filesystem::path &path) {
|
||||
auto info = memgraph::storage::durability::ReadWalInfo(path);
|
||||
memgraph::storage::durability::Decoder wal;
|
||||
wal.Initialize(path, memgraph::storage::durability::kWalMagic);
|
||||
wal.SetPosition(info.offset_deltas);
|
||||
DeltaGenerator::DataT current;
|
||||
for (uint64_t i = 0; i < info.num_deltas; ++i) {
|
||||
auto timestamp = memgraph::storage::durability::ReadWalDeltaHeader(&wal);
|
||||
current.emplace_back(timestamp, memgraph::storage::durability::ReadWalDeltaData(&wal));
|
||||
}
|
||||
ASSERT_EQ(data.size(), current.size());
|
||||
ASSERT_EQ(data, current);
|
||||
}
|
||||
|
||||
class WalFileTest : public ::testing::TestWithParam<bool> {
|
||||
public:
|
||||
WalFileTest() {}
|
||||
|
||||
void SetUp() override { Clear(); }
|
||||
|
||||
void TearDown() override { Clear(); }
|
||||
|
||||
std::vector<std::filesystem::path> GetFilesList() {
|
||||
std::vector<std::filesystem::path> ret;
|
||||
for (auto &item : std::filesystem::directory_iterator(storage_directory)) {
|
||||
ret.push_back(item.path());
|
||||
}
|
||||
std::sort(ret.begin(), ret.end());
|
||||
std::reverse(ret.begin(), ret.end());
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::filesystem::path storage_directory{std::filesystem::temp_directory_path() / "MG_test_unit_storage_v2_wal_file"};
|
||||
|
||||
private:
|
||||
void Clear() {
|
||||
if (!std::filesystem::exists(storage_directory)) return;
|
||||
std::filesystem::remove_all(storage_directory);
|
||||
}
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(EdgesWithProperties, WalFileTest, ::testing::Values(true));
|
||||
INSTANTIATE_TEST_CASE_P(EdgesWithoutProperties, WalFileTest, ::testing::Values(false));
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST_P(WalFileTest, EmptyFile) {
|
||||
{ DeltaGenerator gen(storage_directory, GetParam(), 5); }
|
||||
auto wal_files = GetFilesList();
|
||||
ASSERT_EQ(wal_files.size(), 0);
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
|
||||
#define GENERATE_SIMPLE_TEST(name, ops) \
|
||||
TEST_P(WalFileTest, name) { \
|
||||
memgraph::storage::durability::WalInfo info; \
|
||||
DeltaGenerator::DataT data; \
|
||||
\
|
||||
{ \
|
||||
DeltaGenerator gen(storage_directory, GetParam(), 5); \
|
||||
ops; \
|
||||
info = gen.GetInfo(); \
|
||||
data = gen.GetData(); \
|
||||
} \
|
||||
\
|
||||
auto wal_files = GetFilesList(); \
|
||||
ASSERT_EQ(wal_files.size(), 1); \
|
||||
\
|
||||
if (info.num_deltas == 0) { \
|
||||
ASSERT_THROW(memgraph::storage::durability::ReadWalInfo(wal_files.front()), \
|
||||
memgraph::storage::durability::RecoveryFailure); \
|
||||
} else { \
|
||||
AssertWalInfoEqual(info, memgraph::storage::durability::ReadWalInfo(wal_files.front())); \
|
||||
AssertWalDataEqual(data, wal_files.front()); \
|
||||
} \
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
GENERATE_SIMPLE_TEST(TransactionWithEnd, { TRANSACTION(true, { tx.CreateVertex(); }); });
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
GENERATE_SIMPLE_TEST(TransactionWithoutEnd, { TRANSACTION(false, { tx.CreateVertex(); }); });
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
GENERATE_SIMPLE_TEST(OperationSingle, { OPERATION(LABEL_INDEX_CREATE, "hello"); });
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
GENERATE_SIMPLE_TEST(TransactionsEnd00, {
|
||||
TRANSACTION(false, { tx.CreateVertex(); });
|
||||
TRANSACTION(false, { tx.CreateVertex(); });
|
||||
});
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
GENERATE_SIMPLE_TEST(TransactionsEnd01, {
|
||||
TRANSACTION(false, { tx.CreateVertex(); });
|
||||
TRANSACTION(true, { tx.CreateVertex(); });
|
||||
});
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
GENERATE_SIMPLE_TEST(TransactionsEnd10, {
|
||||
TRANSACTION(true, { tx.CreateVertex(); });
|
||||
TRANSACTION(false, { tx.CreateVertex(); });
|
||||
});
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
GENERATE_SIMPLE_TEST(TransactionsEnd11, {
|
||||
TRANSACTION(true, { tx.CreateVertex(); });
|
||||
TRANSACTION(true, { tx.CreateVertex(); });
|
||||
});
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
GENERATE_SIMPLE_TEST(TransactionsWithOperation_00, {
|
||||
OPERATION(LABEL_INDEX_CREATE, "hello");
|
||||
TRANSACTION(false, { tx.CreateVertex(); });
|
||||
TRANSACTION(false, { tx.CreateVertex(); });
|
||||
});
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
GENERATE_SIMPLE_TEST(TransactionsWithOperation_01, {
|
||||
OPERATION(LABEL_INDEX_CREATE, "hello");
|
||||
TRANSACTION(false, { tx.CreateVertex(); });
|
||||
TRANSACTION(true, { tx.CreateVertex(); });
|
||||
});
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
GENERATE_SIMPLE_TEST(TransactionsWithOperation_10, {
|
||||
OPERATION(LABEL_INDEX_CREATE, "hello");
|
||||
TRANSACTION(true, { tx.CreateVertex(); });
|
||||
TRANSACTION(false, { tx.CreateVertex(); });
|
||||
});
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
GENERATE_SIMPLE_TEST(TransactionsWithOperation_11, {
|
||||
OPERATION(LABEL_INDEX_CREATE, "hello");
|
||||
TRANSACTION(true, { tx.CreateVertex(); });
|
||||
TRANSACTION(true, { tx.CreateVertex(); });
|
||||
});
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
GENERATE_SIMPLE_TEST(TransactionsWithOperation0_0, {
|
||||
TRANSACTION(false, { tx.CreateVertex(); });
|
||||
OPERATION(LABEL_INDEX_CREATE, "hello");
|
||||
TRANSACTION(false, { tx.CreateVertex(); });
|
||||
});
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
GENERATE_SIMPLE_TEST(TransactionsWithOperation0_1, {
|
||||
TRANSACTION(false, { tx.CreateVertex(); });
|
||||
OPERATION(LABEL_INDEX_CREATE, "hello");
|
||||
TRANSACTION(true, { tx.CreateVertex(); });
|
||||
});
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
GENERATE_SIMPLE_TEST(TransactionsWithOperation1_0, {
|
||||
TRANSACTION(true, { tx.CreateVertex(); });
|
||||
OPERATION(LABEL_INDEX_CREATE, "hello");
|
||||
TRANSACTION(false, { tx.CreateVertex(); });
|
||||
});
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
GENERATE_SIMPLE_TEST(TransactionsWithOperation1_1, {
|
||||
TRANSACTION(true, { tx.CreateVertex(); });
|
||||
OPERATION(LABEL_INDEX_CREATE, "hello");
|
||||
TRANSACTION(true, { tx.CreateVertex(); });
|
||||
});
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
GENERATE_SIMPLE_TEST(TransactionsWithOperation00_, {
|
||||
TRANSACTION(false, { tx.CreateVertex(); });
|
||||
TRANSACTION(false, { tx.CreateVertex(); });
|
||||
OPERATION(LABEL_INDEX_CREATE, "hello");
|
||||
});
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
GENERATE_SIMPLE_TEST(TransactionsWithOperation01_, {
|
||||
TRANSACTION(false, { tx.CreateVertex(); });
|
||||
TRANSACTION(true, { tx.CreateVertex(); });
|
||||
OPERATION(LABEL_INDEX_CREATE, "hello");
|
||||
});
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
GENERATE_SIMPLE_TEST(TransactionsWithOperation10_, {
|
||||
TRANSACTION(true, { tx.CreateVertex(); });
|
||||
TRANSACTION(false, { tx.CreateVertex(); });
|
||||
OPERATION(LABEL_INDEX_CREATE, "hello");
|
||||
});
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
GENERATE_SIMPLE_TEST(TransactionsWithOperation11_, {
|
||||
TRANSACTION(true, { tx.CreateVertex(); });
|
||||
TRANSACTION(true, { tx.CreateVertex(); });
|
||||
OPERATION(LABEL_INDEX_CREATE, "hello");
|
||||
});
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
GENERATE_SIMPLE_TEST(AllTransactionOperationsWithEnd, {
|
||||
TRANSACTION(true, {
|
||||
auto vertex1 = tx.CreateVertex();
|
||||
auto vertex2 = tx.CreateVertex();
|
||||
tx.AddLabel(vertex1, "test");
|
||||
tx.AddLabel(vertex2, "hello");
|
||||
tx.SetProperty(vertex2, "hello", memgraph::storage::PropertyValue("nandare"));
|
||||
tx.RemoveLabel(vertex1, "test");
|
||||
tx.SetProperty(vertex2, "hello", memgraph::storage::PropertyValue(123));
|
||||
tx.SetProperty(vertex2, "hello", memgraph::storage::PropertyValue());
|
||||
tx.DeleteVertex(vertex1);
|
||||
});
|
||||
});
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
GENERATE_SIMPLE_TEST(AllTransactionOperationsWithoutEnd, {
|
||||
TRANSACTION(false, {
|
||||
auto vertex1 = tx.CreateVertex();
|
||||
auto vertex2 = tx.CreateVertex();
|
||||
tx.AddLabel(vertex1, "test");
|
||||
tx.AddLabel(vertex2, "hello");
|
||||
tx.SetProperty(vertex2, "hello", memgraph::storage::PropertyValue("nandare"));
|
||||
tx.RemoveLabel(vertex1, "test");
|
||||
tx.SetProperty(vertex2, "hello", memgraph::storage::PropertyValue(123));
|
||||
tx.SetProperty(vertex2, "hello", memgraph::storage::PropertyValue());
|
||||
tx.DeleteVertex(vertex1);
|
||||
});
|
||||
});
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
GENERATE_SIMPLE_TEST(AllGlobalOperations, {
|
||||
OPERATION(LABEL_INDEX_CREATE, "hello");
|
||||
OPERATION(LABEL_INDEX_DROP, "hello");
|
||||
OPERATION(LABEL_PROPERTY_INDEX_CREATE, "hello", {"world"});
|
||||
OPERATION(LABEL_PROPERTY_INDEX_DROP, "hello", {"world"});
|
||||
OPERATION(EXISTENCE_CONSTRAINT_CREATE, "hello", {"world"});
|
||||
OPERATION(EXISTENCE_CONSTRAINT_DROP, "hello", {"world"});
|
||||
OPERATION(UNIQUE_CONSTRAINT_CREATE, "hello", {"world", "and", "universe"});
|
||||
OPERATION(UNIQUE_CONSTRAINT_DROP, "hello", {"world", "and", "universe"});
|
||||
});
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
GENERATE_SIMPLE_TEST(InvalidTransactionOrdering, {
|
||||
TRANSACTION(true, { tx.CreateVertex(); });
|
||||
TRANSACTION(true, { tx.CreateVertex(); });
|
||||
TRANSACTION(true, { tx.CreateVertex(); });
|
||||
gen.ResetTransactionIds();
|
||||
TRANSACTION(true, { tx.CreateVertex(); });
|
||||
});
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST_P(WalFileTest, InvalidMarker) {
|
||||
memgraph::storage::durability::WalInfo info;
|
||||
|
||||
{
|
||||
DeltaGenerator gen(storage_directory, GetParam(), 5);
|
||||
TRANSACTION(true, { tx.CreateVertex(); });
|
||||
info = gen.GetInfo();
|
||||
}
|
||||
|
||||
auto wal_files = GetFilesList();
|
||||
ASSERT_EQ(wal_files.size(), 1);
|
||||
const auto &wal_file = wal_files.front();
|
||||
|
||||
auto final_info = memgraph::storage::durability::ReadWalInfo(wal_file);
|
||||
AssertWalInfoEqual(info, final_info);
|
||||
|
||||
size_t i = 0;
|
||||
for (auto marker : memgraph::storage::durability::kMarkersAll) {
|
||||
if (marker == memgraph::storage::durability::Marker::SECTION_DELTA) continue;
|
||||
auto current_file = storage_directory / fmt::format("temporary_{}", i);
|
||||
ASSERT_TRUE(std::filesystem::copy_file(wal_file, current_file));
|
||||
memgraph::utils::OutputFile file;
|
||||
file.Open(current_file, memgraph::utils::OutputFile::Mode::OVERWRITE_EXISTING);
|
||||
file.SetPosition(memgraph::utils::OutputFile::Position::SET, final_info.offset_deltas);
|
||||
auto value = static_cast<uint8_t>(marker);
|
||||
file.Write(&value, sizeof(value));
|
||||
file.Sync();
|
||||
file.Close();
|
||||
ASSERT_THROW(memgraph::storage::durability::ReadWalInfo(current_file),
|
||||
memgraph::storage::durability::RecoveryFailure);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST_P(WalFileTest, PartialData) {
|
||||
std::vector<std::pair<uint64_t, memgraph::storage::durability::WalInfo>> infos;
|
||||
|
||||
{
|
||||
DeltaGenerator gen(storage_directory, GetParam(), 5);
|
||||
TRANSACTION(true, { tx.CreateVertex(); });
|
||||
infos.emplace_back(gen.GetPosition(), gen.GetInfo());
|
||||
TRANSACTION(true, {
|
||||
auto vertex = tx.CreateVertex();
|
||||
tx.AddLabel(vertex, "hello");
|
||||
});
|
||||
infos.emplace_back(gen.GetPosition(), gen.GetInfo());
|
||||
OPERATION(LABEL_PROPERTY_INDEX_CREATE, "hello", {"world"});
|
||||
infos.emplace_back(gen.GetPosition(), gen.GetInfo());
|
||||
TRANSACTION(true, {
|
||||
auto vertex1 = tx.CreateVertex();
|
||||
auto vertex2 = tx.CreateVertex();
|
||||
tx.AddLabel(vertex1, "test");
|
||||
tx.AddLabel(vertex2, "hello");
|
||||
tx.SetProperty(vertex2, "hello", memgraph::storage::PropertyValue("nandare"));
|
||||
tx.RemoveLabel(vertex1, "test");
|
||||
tx.SetProperty(vertex2, "hello", memgraph::storage::PropertyValue(123));
|
||||
tx.SetProperty(vertex2, "hello", memgraph::storage::PropertyValue());
|
||||
tx.DeleteVertex(vertex1);
|
||||
});
|
||||
infos.emplace_back(gen.GetPosition(), gen.GetInfo());
|
||||
}
|
||||
|
||||
auto wal_files = GetFilesList();
|
||||
ASSERT_EQ(wal_files.size(), 1);
|
||||
const auto &wal_file = wal_files.front();
|
||||
|
||||
AssertWalInfoEqual(infos.back().second, memgraph::storage::durability::ReadWalInfo(wal_file));
|
||||
|
||||
auto current_file = storage_directory / "temporary";
|
||||
memgraph::utils::InputFile infile;
|
||||
infile.Open(wal_file);
|
||||
|
||||
uint64_t pos = 0;
|
||||
for (size_t i = 0; i < infile.GetSize(); ++i) {
|
||||
if (i < infos.front().first) {
|
||||
ASSERT_THROW(memgraph::storage::durability::ReadWalInfo(current_file),
|
||||
memgraph::storage::durability::RecoveryFailure);
|
||||
} else {
|
||||
if (i >= infos[pos + 1].first) ++pos;
|
||||
AssertWalInfoEqual(infos[pos].second, memgraph::storage::durability::ReadWalInfo(current_file));
|
||||
}
|
||||
{
|
||||
memgraph::utils::OutputFile outfile;
|
||||
outfile.Open(current_file, memgraph::utils::OutputFile::Mode::APPEND_TO_EXISTING);
|
||||
uint8_t value;
|
||||
ASSERT_TRUE(infile.Read(&value, sizeof(value)));
|
||||
outfile.Write(&value, sizeof(value));
|
||||
outfile.Sync();
|
||||
outfile.Close();
|
||||
}
|
||||
}
|
||||
ASSERT_EQ(pos, infos.size() - 2);
|
||||
AssertWalInfoEqual(infos[infos.size() - 1].second, memgraph::storage::durability::ReadWalInfo(current_file));
|
||||
}
|
Loading…
Reference in New Issue
Block a user